Quelle meilleure façon de mettre en œuvre print_last_arg
?
#!/bin/sh
print_last_arg () {
eval "echo \${$#}" # this hurts
}
print_last_arg foo bar baz
# baz
(Si c'était le cas, par exemple, #!/usr/bin/zsh
au lieu de #!/bin/sh
je saurais quoi faire. Mon problème est de trouver un moyen raisonnable de mettre cela en œuvre #!/bin/sh
.)
EDIT: Ce qui précède n'est qu'un exemple stupide. Mon objectif n'est pas d' imprimer le dernier argument, mais plutôt d'avoir un moyen de faire référence au dernier argument dans une fonction shell.
EDIT2: Je m'excuse pour une question si peu formulée. J'espère bien faire les choses cette fois.
Si tel était au /bin/zsh
lieu de /bin/sh
, je pourrais écrire quelque chose comme ça
#!/bin/zsh
print_last_arg () {
local last_arg=$argv[$#]
echo $last_arg
}
L'expression $argv[$#]
est un exemple de ce que j'ai décrit dans mon premier EDIT comme un moyen de faire référence au dernier argument d'une fonction shell .
Par conséquent, j'aurais vraiment dû écrire mon exemple original comme ceci:
print_last_arg () {
local last_arg=$(eval "echo \${$#}") # but this hurts even more
echo $last_arg
}
... pour préciser que ce que je recherche est une chose moins horrible à mettre à droite de la mission.
Notez cependant que dans tous les exemples, le dernier argument est accessible de manière non destructive . IOW, l'accès au dernier argument ne modifie pas les arguments positionnels dans leur ensemble.
var=$( eval echo \${$#})
àeval var=\${$#}
- les deux ne sont rien pareils.shift
etset -- ...
solutions pourraient être destructrices moins utilisées dans les fonctions où ils sont trop inoffensifs.eval "var=\${$#}"
par rapport à,var=${arr[evaled index]}
sauf qu'il$#
s'agit d'une valeur sûre garantie. pourquoi copier l'ensemble entier puis le détruire alors que vous pouvez simplement l'indexer directement?Réponses:
... affichera la chaîne
the last arg is
suivie d'un <espace> , de la valeur du dernier argument et d'un <newline> de fin s'il y a au moins 1 argument, ou bien, pour zéro argument, il n'imprimera rien du tout.Si vous l'avez fait:
... alors soit vous assigneriez la valeur du dernier argument à la variable shell
$lastarg
s'il y a au moins 1 argument, soit vous ne feriez rien du tout. De toute façon, vous le feriez en toute sécurité, et il devrait être portable même pour le shell Olde Bourne, je pense.Voici un autre qui fonctionnerait de la même manière, bien qu'il nécessite de copier deux fois le tableau d'arg entier (et nécessite un
printf
in$PATH
pour le shell Bourne) :la source
${1+":"} return
suite - car qui veut qu'elle fasse quoi que ce soit ou risque des effets secondaires de quelque nature que ce soit si rien n'était demandé? Les mathématiques sont identiques pour la même chose - si vous pouvez être sûr que vous pouvez développer un entier positif, vous pouvezeval
toute la journée.$OPTIND
est super pour ça.Voici une manière simpliste:
(mis à jour en fonction du point de @ cuonglm selon lequel l'original a échoué lorsqu'il n'a pas été passé d'arguments; cela fait maintenant écho à une ligne vierge - modifiez ce comportement dans la
else
clause si vous le souhaitez)la source
echo "$# - 1" | bc
Étant donné l'exemple du message d'ouverture (arguments positionnels sans espaces):
Par défaut
IFS=' \t\n'
, que diriez-vous:Pour une extension plus sûre de
"$*"
, réglez IFS (per @ StéphaneChazelas):Mais ce qui précède échouera si vos arguments positionnels peuvent contenir des espaces. Dans ce cas, utilisez plutôt ceci:
Notez que ces techniques évitent l'utilisation de
eval
et n'ont pas d'effets secondaires.Testé sur shellcheck.net
la source
$IFS
est un espace.IFS=' '
ici simplement pour indiquer clairement qu'il est utilisé pour l'expansion de"$*"
.Bien que cette question ait un peu plus de 2 ans, je pensais partager une option un peu plus compacte.
Lançons-le
Expansion des paramètres du shell Bash .
Éditer
Encore plus concis:
echo "${@: -1}"
(Attention à l'espace)
La source
Testé sur macOS 10.12.6 mais devrait également renvoyer le dernier argument sur la plupart des versions * nix disponibles ...
Ça fait moins mal
¯\_(ツ)_/¯
la source
echo "${*: -1}"
quishellcheck
ne se plaindra pas.${array:n:m:}
est une extension. (la question a mentionné explicitement/bin/sh
)POSIX:
(Cette approche fonctionne également dans le vieux shell Bourne)
Avec d'autres outils standard:
(Cela ne fonctionnera pas avec l'ancien
awk
, qui n'avait pasARGV
)la source
awk
pourrait aussi être l'ancien awk qui n'en avait pasARGV
.awk
approche, ou utilisez une autrefor
boucle simple comme les autres, ou passez-les à une fonction.Cela devrait fonctionner avec n'importe quel shell compatible POSIX et fonctionnera également avec le shell Solaris Bourne antérieur à POSIX:
et voici une fonction basée sur la même approche:
PS: ne me dites pas que j'ai oublié les accolades autour du corps de la fonction ;-)
la source
in ...
dans unefor
boucle et un pas d'accolades autour d'uneif
instruction utilisée comme corps de fonction. Voirfor_clause
etcompound_command
dans pubs.opengroup.org/onlinepubs/9699919799 .De "Unix - Foire aux questions"
(1)
Si le nombre d'arguments peut être nul, l'argument zéro
$0
(généralement le nom du script) sera attribué à$last
. C'est la raison du si.(2)
(3)
Pour éviter d'imprimer une ligne vide en l'absence d'arguments, remplacez le
echo "$last"
for:Un nombre d'arguments nul est évité par
if [ $# -gt 0 ]
.Ce n'est pas une copie exacte de ce qui se trouve dans le lien dans la page, certaines améliorations ont été ajoutées.
la source
Cela devrait fonctionner sur tous les systèmes avec
perl
installé (donc la plupart des UNICES):L'astuce consiste à
printf
ajouter un\0
après chaque argument shell et ensuiteperl
le-0
commutateur qui définit son séparateur d'enregistrement sur NULL. Ensuite, nous itérons sur l'entrée, supprimons le\0
et enregistrons chaque ligne terminée par NULL sous$l
. LeEND
bloc (c'est ce que}{
c'est) sera exécuté une fois que toutes les entrées auront été lues, donc affichera la dernière "ligne": le dernier argument du shell.la source
sed
,awk
ouperl
alors il pourrait être des informations précieuses de toute façon. vous pouvez toujours faire la même chosesed -z
pour les versions GNU à jour.Voici une version utilisant la récursivité. Je ne sais pas à quel point c'est conforme à POSIX ...
la source
Un concept simple, pas d'arithmétique, pas de boucle, pas d' évaluation , juste des fonctions.
N'oubliez pas que le shell Bourne n'avait pas d'arithmétique (nécessaire externe
expr
). Si vous souhaitez obtenir une arithmétique libre,eval
libre choix, c'est une option. Besoin de fonctions signifie SVR3 ou supérieur (pas d'écrasement des paramètres).Regardez ci-dessous pour une version plus robuste avec printf.
Cette structure d'appel
printlast
est fixé , les arguments doivent être définis dans la liste des arguments shell$1
,$2
etc. (la pile d'arguments) et l'appel fait comme indiqué.Si la liste des arguments doit être modifiée, il suffit de les définir:
Ou créez une fonction (
getlast
) plus facile à utiliser qui pourrait autoriser des arguments génériques (mais pas aussi rapidement, les arguments sont passés deux fois).Veuillez noter que les arguments (de
getlast
, ou tous inclus dans$@
pour printlast) peuvent avoir des espaces, des retours à la ligne, etc. Mais pas NUL.Meilleur
Cette version ne s'imprime pas
0
lorsque la liste des arguments est vide et utilise le printf plus robuste (revenir àecho
si externalprintf
n'est pas disponible pour les anciens shells).Utilisation d'EVAL.
Si le shell Bourne est encore plus ancien et qu'il n'y a pas de fonctions, ou si pour une raison quelconque, utiliser eval est la seule option:
Pour imprimer la valeur:
Pour définir la valeur dans une variable:
Si cela doit être fait dans une fonction, les arguments doivent être copiés dans la fonction:
la source
eval
?for loop
ouwhile loop
?echo "Hello"
a des boucles car le CPU doit lire les emplacements de mémoire dans une boucle. Encore une fois: mon code n'a pas de boucles ajoutées.for
,while
ou desuntil
boucles. En voyez-vous unefor
while
ou uneuntil
boucle écrite dans le code?. Non ?, alors acceptez simplement le fait, acceptez-le et apprenez.Ce commentaire a suggéré d'utiliser le très élégant:
la source