Traiter tous les arguments sauf le premier (dans un script bash)

444

J'ai un script simple où le premier argument est réservé pour le nom de fichier, et tous les autres arguments facultatifs doivent être passés à d'autres parties du script.

En utilisant Google, j'ai trouvé ce wiki , mais il a fourni un exemple littéral:

echo "${@: -1}"

Je ne peux rien faire d'autre, comme:

echo "${@:2}"

ou

echo "${@:2,1}"

J'obtiens "Mauvaise substitution" du terminal.

Quel est le problème et comment puis-je traiter tout sauf le premier argument passé à un script bash?

thêta
la source
21
Pour appeler quelqu'un d'autre confus, le mauvais shebang a été fourni, ce "{@:2}"qui a provoqué un échec , c'est pourquoi la bonne réponse correspond ci-dessus.
Guvante
3
Vous venez d'utiliser le shell par défaut, qui est un tiret sur Ubuntu et de nombreux autres Linux. Dans le tiret "$ {@: -1}" est interprété comme: {paramètre: -word} - Utilisez les valeurs par défaut, et utilisez le mot si le paramètre n'est pas défini ou nul. Ainsi, dans le tiret "$ {@: -1}", les résultats sont exactement les mêmes que "$ @". Pour utiliser bash, utilisez simplement la première ligne suivante dans le fichier de script: #! / Bin / bash
luart

Réponses:

661

Utilisez ceci:

echo "${@:2}"

La syntaxe suivante:

echo "${*:2}"

fonctionnerait également, mais n'est pas recommandé, car comme @Gordon l'a déjà expliqué, l'utilisation *, il exécute tous les arguments ensemble comme un seul argument avec des espaces, tout en @préservant les interruptions entre eux (même si certains des arguments eux-mêmes contiennent des espaces ). Cela ne fait pas la différence avec echo, mais c'est important pour de nombreuses autres commandes.

Oliver Charlesworth
la source
7
Je viens de réaliser que mon shebang était mauvais: #!/usr/bin/env shc'est pourquoi j'ai eu des problèmes. Votre exemple fonctionne bien, comme ci-dessus, après avoir retiré ce shebang
theta
110
Utilisez à la "${@:2}"place - l'utilisation *exécute tous les arguments ensemble comme un seul argument avec des espaces, tout en @préservant les sauts entre eux (même si certains des arguments eux-mêmes contiennent des espaces). La différence n'est pas perceptible avec echo, mais elle compte pour bien d'autres choses.
Gordon Davisson
2
@GordonDavisson Le but ici est d'exécuter les arguments ensemble. Si nous transmettions des noms de fichiers, vous auriez raison. echoest assez indulgent pour les concaténer pour vous; d'autres commandes pourraient ne pas être aussi agréables. Ne vous contentez pas d'utiliser l'un ou l'autre: apprenez la différence entre *et @et quand les utiliser. Vous devriez les utiliser à peu près également. Un bon exemple de quand ce sera un problème: si $3contient un saut de ligne ( \n), il sera remplacé par un espace, à condition que vous ayez une $IFSvariable par défaut .
Zenexer
1
@Zenexer: Si je comprends bien, la question était de savoir comment passer tout sauf le premier argument à "vers une autre partie du script", avec echojuste utilisé comme exemple - auquel cas ils ne devraient pas être exécutés ensemble. D'après mon expérience, les situations où vous voulez les faire fonctionner ensemble sont rares (voir cette question pour un exemple ), et "$@"c'est presque toujours ce que vous voulez. En outre, le problème que vous mentionnez avec un saut de ligne se produit uniquement si $@(ou $*) n'est pas entre guillemets.
Gordon Davisson
@GordonDavisson Hmm ... vous avez raison, maintenant que j'y pense; le saut de ligne ne devrait pas être un problème. Je devais penser à autre chose. Cependant, je dois encore être en désaccord sur leur fonctionnement ensemble; Je dois utiliser $*assez souvent dans mes scripts.
Zenexer
180

Si vous voulez une solution qui fonctionne également dans /bin/shtry

first_arg="$1"
shift
echo First argument: "$first_arg"
echo Remaining arguments: "$@"

shift [n]décale les paramètres de position n fois. A shiftdéfinit la valeur de $1sur la valeur de $2, la valeur de $2sur la valeur de $3, etc., en diminuant la valeur de $#un.

Ben Jackson
la source
25
+1: Vous devriez probablement enregistrer 1 $ dans une variable avant de la déplacer.
glenn jackman
3
Étonnamment foo=shiftne fait pas ce à quoi je m'attendais.
Keith Smiley
Je sais que c'est vieux, mais essayezfoo=$(shift)
raumaan kidwai
12
@raumaankidwai Cela ne fonctionne pas pour 2 raisons: 1) shift(dans le shell) n'a pas de sortie. Il jette tout simplement $1et décale tout. 2) $(...)démarre un sous-shell, qui a ses propres arguments locaux. Il déplace les arguments dans le sous-shell, ce qui n'affecte pas le parent
Ben Jackson
Merci :) Existe-t-il un moyen de faire quoi somecommand "$1" "${@:2}"avec cette méthode (c.-à-d. Déplacer "en ligne")?
Anonyme
0

Je suis tombé sur cette recherche de quelque chose d'autre. Bien que la publication semble assez ancienne, la solution la plus simple dans bash est illustrée ci-dessous (au moins bash 4) en utilisant set -- "${@:#}"où # est le numéro de départ de l'élément de tableau que nous voulons conserver en avant:

    #!/bin/bash

    someVar="${1}"
    someOtherVar="${2}"
    set -- "${@:3}"
    input=${@}

    [[ "${input[*],,}" == *"someword"* ]] && someNewVar="trigger"

    echo -e "${someVar}\n${someOtherVar}\n${someNewVar}\n\n${@}"

Fondamentalement, le set -- "${@:3}"juste saute les deux premiers éléments du tableau comme le décalage de perl et préserve tous les éléments restants, y compris le troisième. Je soupçonne qu'il existe également un moyen de masquer les derniers éléments.

Pavman
la source