L'expansion des paramètres sur $ @ n'est-elle pas prise en charge par le shell sh?

8

J'ai posté une réponse à une question sur AU, et j'ai trouvé que l'expansion des paramètres sur $@ne fonctionne pas avec le shshell:

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

mais ça marche bien bash. Est-ce le comportement attendu du shshell, et comment puis-je y effectuer une expansion?

De plus, je sais qu'avec l' -n1option de xargsje ne peux passer qu'une seule ligne à la commande à la fois, mais je voulais savoir si je pouvais shdévelopper $@:

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

infile contient:

A1 /B1/C1
A 2/B2/C2
A3/B3/C3
αғsнιη
la source

Réponses:

12

Oui, le tiret semble moins utile ici. Bien qu'il ne soit pas en faute, à proprement parler, comme cela ${@%...}n'est pas spécifié par POSIX :

Les quatre variétés d'expansion de paramètres suivantes permettent le traitement de la sous-chaîne. [...] Si le paramètre est ' #', ' *' ou ' @', le résultat de l'expansion n'est pas spécifié.

C'est bizarre cependant, il semble que si une expansion comme celle-ci modifie la fin d'un paramètre positionnel, elle supprime les suivants. Mais pas s'il ne modifie pas réellement la fin:

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

Bash, ksh et Zsh semblent tous gérer "${@#...}"et "${@%...}"en traitant indépendamment chaque paramètre de position, ce qui semble la chose utile à faire.

Je suppose que la solution de contournement évidente consiste dashà effectuer la modification un argument à la fois:

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

Pour ce que ça vaut, le comportement des extensions de suppression de préfixe / suffixe utilisées $*varie également entre les shells. Bash et ksh semblent modifier les paramètres en premier et les rejoindre ensuite, tandis que Zsh et dash rejoignent les paramètres en premier et modifient la chaîne concaténée:

$ zsh -c 'set -- ax bx; printf "<%s>\n" "${*%%x*}";'
<a>
$ bash -c 'set -- ax bx; printf "<%s>\n" "${*%%x*}";'
<a b>
ilkkachu
la source
Sons shpense que $@c'est un paramètre unique pour tout le fichier (ou se divisera en plusieurs si dépassent ARG_MAX) et fait une expansion sur le seul argument.
αғsнιη
1
Notez également que pdksh et ses dérivés vous donnent une Bad substitutionerreur sur ce code. Car ${*%pattern}, vous voyez des variations de comportement dans des choses comme"$shell" -c 'printf "<%s>\n" "${*%x*}"' sh ax by
Stéphane Chazelas
1
@ αғsнιη, ce n'est pas si simple. notez le deuxième exemple où l'expansion laisse les paramètres positionnels comme des mots séparés. Et je ne pense pas que cela ARG_MAXentre en jeu, que le traitement est interne au shell.
ilkkachu