Jak korzystać z $ *, pomijając pewne zmienne wejściowe, takie jak 1 $ i 2 $ w skrypcie bash?

15

Na przykład,

elif [[ $append = $1 ]]
then
  touch ~/directory/"$2".txt
  echo "$variable_in_question" >> ~/directory/"$2".txt

albo utworzyć plik tekstowy zawierający wszystkie dane wejściowe po nim "$2", albo dołączyć istniejący plik tekstowy z wszystkimi "$2"danymi wejściowymi następującymi , czego miałbym użyć zamiast"$variable_in_question" wiersza 4?

Zasadniczo chcę "$*", ale pomijam "$1"i "$2".

Oreoplazma
źródło

Odpowiedzi:

32

Można użyć bashrozszerzenia parametru do określenia zakresu, działa to również z parametrami pozycyjnymi. Dla $3$nbyłoby to:

"${@:3}" # expands to "$3" "$4" "$5" …
"${*:3}" # expands to "$3 $4 $5 …"

Pamiętaj o tym $@i $*zignoruj ​​pierwszy argument $0. Jeśli zastanawiasz się, którego użyć w twoim przypadku: jest bardzo prawdopodobne, że chcesz otrzymać ofertę $@. Nie używaj, $*chyba że wyraźnie nie chcesz, aby argumenty były cytowane indywidualnie.

Możesz to wypróbować w następujący sposób:

$ bash -c 'echo "${@:3}"' 0 1 2 3 4 5 6
3 4 5 6
$ echo 'echo "${@:3}"' >script_file
$ bash script_file 0 1 2 3 4 5 6
2 3 4 5 6

Zauważ, że w pierwszym przykładzie $0jest wypełniony pierwszym argumentem, 0podczas gdy gdy jest używany w skrypcie, $0zamiast tego jest wypełniany nazwą skryptu, jak pokazuje drugi przykład. Nazwa skryptu do bashoczywiście jest pierwszym argumentem, tylko że to nie zwykle postrzegany jako taki - to samo dotyczy skrypt wykonywalny i nazywa się „bezpośrednio”. Tak więc w pierwszym przykładzie mamy $0= 0, $1= 1itd., Podczas gdy w drugim jest to $0= script_file, $1= 0, $2= 1itd .; ${@:3}wybiera każdy argument zaczynający się od $3.

Kilka dodatkowych przykładów możliwych zakresów:

 # two arguments starting with the third
$ bash -c 'echo "${@:3:2}"' 0 1 2 3 4 5 6
3 4
 # every argument starting with the second to last one
 # a negative value needs either a preceding space or parentheses
$ bash -c 'echo "${@: -2}"' 0 1 2 3 4 5 6
5 6
 # two arguments starting with the fifth to last one
$ bash -c 'echo "${@:(-5):2}"' 0 1 2 3 4 5 6
2 3

Dalsza lektura:

deser
źródło
27

Możesz użyć shift wbudowanego:

$ help shift
shift: shift [n]
    Shift positional parameters.

    Rename the positional parameters $N+1,$N+2 ... to $1,$2 ...  If N is
    not given, it is assumed to be 1.

    Exit Status:
    Returns success unless N is negative or greater than $#.

Dawny. dany

$ cat argtest.bash 
#!/bin/bash

shift 2

echo "$*"

następnie

$ ./argtest.bash foo bar baz bam boo
baz bam boo
steeldriver
źródło
3
@Oreoplasm Myślę, że warto wspomnieć, że shiftpodejście uniemożliwi dostęp $1i $2po ich przesunięciu. W skrypcie, którego używasz $2obok $variable_in_question, musisz to zmienić lub zastosować metodę rozszerzania parametrów.
deser
6
shiftjest dobra, kiedy pierwszy jeden lub więcej argumentów są wyjątkowe i ma to sens, aby odebrać je w oddzielnych zmiennych ( foo="$1"; bar="$2";lub if [[ something with $1 ]];then blah blah; shift. Ale @ deser za odpowiedź z nieniszczących podejścia jest miła w innych przypadkach, gdy ma nadal chcą pełną listę później lub gdy używasz bardziej zaawansowanej składni do wybierania ograniczonego zakresu argumentów, nie do nieskończoności, bez wprowadzania pustych argumentów do polecenia, jeśli $@nie ma tak wielu elementów
Peter Cordes
13

Ogólnie rzecz biorąc, możesz skopiować parametry pozycyjne do tablicy, usunąć dowolne indeksy tablicy, a następnie użyć tablicy do rozwinięcia dokładnie tych wskaźników, które chcesz, bez utraty oryginalnych argumentów.

Na przykład, gdybym chciał wszystkich argumentów oprócz pierwszego, czwartego i piątego:

args=( "$@" )
unset args[0] args[3] args[4]
echo "${args[@]}"

W kopii indeksy są przesunięte o 1, ponieważ $0nie jest częścią $@.

muru
źródło
1
Oczywiście można to połączyć, np. echo "${args[@]:2}"Dla trzeciego do ostatniego argumentu.
deser