I posted an answer to a question on AU, and I found that parameter expansion on $@ doesn't work with the sh shell:

<infile xargs -d'\n' sh -c 'echo "${@%%/*}"' _

but it works fine in bash. Is this expected behavior of the sh shell, and how can I perform expansion there?

Additionally, I know that with the -n1 option of xargs I can pass only one line to the command at a time, but I was interested in whether sh can expand $@:

<infile xargs -d'\n' -n1 sh -c 'echo "${0%%/*}"'

infile contains:

A1 /B1/C1
A 2/B2/C2

Yep, dash appears to be less than useful here. Though it's not at fault, strictly speaking, as ${@%...} is unspecified by POSIX:

The following four varieties of parameter expansion provide for substring processing. [...] If parameter is '#', '*', or '@', the result of the expansion is unspecified.

It's weird though, it seems that if an expansion like that modifies the end of one positional parameter, it drops the following ones. But not if it doesn't actually modify the end:

$ dash -c 'set -- foo bar; printf "<%s>\n" "${@%o}";'
$ dash -c 'set -- foo bar; printf "<%s>\n" "${@%x}";'
$ dash -c 'set -- foo bar doo; printf "<%s>\n" "${@%r}";'

Bash, ksh and Zsh all seem to handle "${@#...}" and "${@%...}" by processing each positional parameter independently, which would seem the useful thing to do.

I suppose the obvious workaround for dash is to make the modification one argument at a time:

for x in "$@"; do echo "${x%%/*}"; done

For what it's worth, the behaviour of the prefix/suffix removal expansions used on $* also varies between shells. Bash and ksh seem to modify the parameters first and join them after that, while Zsh and dash join the parameters first and modify the concatenated string:

$ zsh -c 'set -- ax bx; printf "<%s>\n" "${*%%x*}";'
$ bash -c 'set -- ax bx; printf "<%s>\n" "${*%%x*}";'
<a b>
  • sounds sh thinks $@ is a single parameter for whole file (or will break to multiple if exceed ARG_MAX) and do expansion on the only argument. – αғsнιη May 24 '18 at 10:44
  • 1
    Also note that pdksh and derivatives give you a Bad substitution error on that code. For ${*%pattern}, you see some variation in behaviour in things like "$shell" -c 'printf "<%s>\n" "${*%x*}"' sh ax by – Stéphane Chazelas May 24 '18 at 10:52
  • 1
    @αғsнιη, it's not that simple. note the second example where the expansion leaves the positional parameters as separate words. And I don't think ARG_MAX comes into it, that processing is internal to the shell. – ilkkachu May 24 '18 at 10:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.