Skrypt Bash do odbierania i ponownego przypisywania cytowanych parametrów

98

Próbuję uzyskać cytowane parametry skryptu bash, aby były bezpiecznie odbierane przez zagnieżdżony skrypt. Jakieś pomysły?

test.sh

#!/bin/bash
echo $*
bash myecho.sh $*

myecho.sh

#!/bin/bash
 echo $1
 echo $2
 echo $3
 echo $4

Próba:

bash test.sh aaa bbb '"ccc ddd"'

Wynik:

aaa bbb "ccc ddd"
aaa
bbb
"ccc
ddd"

Oczekiwany wynik

aaa bbb "ccc ddd"
aaa
bbb
ccc ddd
chilltemp
źródło
2
Właśnie miałem zadać to pytanie! Dobre wyczucie czasu.
Scottie T

Odpowiedzi:

70
#!/bin/bash
echo $*
bash myecho.sh "$@"

Zauważ, że konstrukcja "$ @" nie jest specyficzna dla basha i powinna działać z każdą powłoką POSIX (działa przynajmniej z myślnikiem). Zauważ również, że biorąc pod uwagę wyniki, które chcesz, w ogóle nie potrzebujesz dodatkowego poziomu cytowania. IE po prostu wywołaj powyższy skrypt:

./test.sh 1 2 "3 4"
pixelbeat
źródło
5
„$ @” działa z każdą powłoką Bourne'a lub pochodną powłoki Bourne'a (od 1978 roku), w tym Korn i Bash. Prawdopodobnie w 95% przypadków użycie „$ @” jest poprawne, a $ * błędne.
Jonathan Leffler
Miły! Ale czy znasz kogoś, czy istnieje sposób, aby zapisać to „tak jak jest” w zmiennej? Oryginał $@nie jest dostępny wewnątrz funkcji (ponieważ jest nadpisywany przez argumenty funkcji). Próbowałem foovar="$@"i foovar=$@+ "$foovar"w funkcji i żadna nie działała: - /
bitifet
143

Chcesz użyć „$ @” (w dolarach w cudzysłowie) do przekazywania parametrów do indeksu dolnego. Tak jak tak ...

ls-color.sh:

#!/bin/bash
/bin/ls --color=auto "$@"    # passes though all CLI-args to 'ls'


Dlaczego .....

Ze strony podręcznika Bash :

$*- rozwija się do parametrów pozycyjnych, zaczynając od jednego. Gdy interpretacja występuje w cudzysłowach, jest interpretowana jako pojedyncze słowo z wartością każdego parametru oddzieloną pierwszym znakiem zmiennej specjalnej IFS. Oznacza to, że "$*"jest równoważne z "$1c$2c...", gdzie c jest pierwszym znakiem wartości zmiennej IFS. Jeśli IFS nie jest ustawiony, parametry są oddzielane spacjami. Jeśli IFS ma wartość null, parametry są łączone bez separatorów.

$@- rozwija się do parametrów pozycyjnych, zaczynając od jednego. Kiedy interpretacja występuje w cudzysłowach, każdy parametr jest interpretowany jako oddzielne słowo. To znaczy "$@"jest równoważne z "$1" "$2" ...Jeśli interpretacja w cudzysłowie występuje w słowie, interpretacja pierwszego parametru jest łączona z początkową częścią oryginalnego słowa, a interpretacja ostatniego parametru jest łączona z ostatnią częścią oryginalnego słowa słowo. Gdy nie ma parametrów pozycyjnych "$@"i nie $@rozwija się do niczego (tj. Są usuwane).


Konfigurowanie niektórych skryptów demonstracyjnych ...

echo 'echo -e "\$1=$1\n\$2=$2\n\$3=$3\n\$4=$4"' > echo-params.sh
echo './echo-params.sh $*' > dollar-star.sh
echo './echo-params.sh $@' > dollar-at.sh
echo './echo-params.sh "$*"' > quoted-dollar-star.sh
echo './echo-params.sh "$@"' > quoted-dollar-at.sh
chmod +x *.sh

"$@"- quoted-dollar-at to transformacja tożsamości w celu ponownego przekazania argumentów do podpowłoki (w ~ 99% przypadków to właśnie zamierzałeś zrobić):

./quoted-dollar-at.sh aaa '' "'cc cc'" '"ddd ddd"'
  # $1= aaa
  # $2=            
  # $3= 'cc cc'
  # $4= "ddd ddd"

"$*"- quoted-dollar-star rozbija argumenty w jeden ciąg (~ 1% czasu, w którym faktycznie chcesz to zachowanie, np. w warunku if [[ -z "$*" ]]; then ...:):

./quoted-dollar-star.sh aaa '' "'cc cc'" '"ddd ddd"'
  # $1= aaa  'cc cc' "ddd ddd"   
  # $2=                     
  # $3=             
  # $4=

$*/ $@- bez cudzysłowów, oba formularze usuwają jeden poziom cudzysłowu i interpretują spacje z łańcuchów bazowych, ale ignorują znaki cudzysłowu (prawie zawsze jest to błąd):

./dollar-star.sh aaa '' "'cc cc'" '"ddd ddd"'
  # $1= aaa
  # $2= 'cc                  
  # $3= cc'
  # $4= "ddd

./dollar-at.sh aaa '' "'cc cc'" '"ddd ddd"'
  # $1= aaa
  # $2= 'cc
  # $3= cc'
  # $4= "ddd

Jeśli chcesz się trochę zabawić, możesz użyć "$ @", aby zagnieździć rzeczy tak głęboko, jak chcesz, wypychając i zdejmując elementy ze stosu argumentów, jeśli chcesz.

function identity() {
  "$@"
}
set -x
identity identity identity identity identity echo Hello \"World\"
# + identity identity identity identity identity echo Hello '"World"'
# + identity identity identity identity echo Hello '"World"'
# + identity identity identity echo Hello '"World"'
# + identity identity echo Hello '"World"'
# + identity echo Hello '"World"'
# + echo Hello '"World"'
# Hello "World"
Dave Dopson
źródło
1
Dziękuję za wyjaśnienie. Właśnie użyłem „$ *” jako aliasu grep.
mroku
Ratujesz mój dzień!
xyz,