Skrip Bash untuk menerima dan memperbaiki parameter yang dikutip

98

Saya mencoba mendapatkan parameter kutipan dari skrip bash agar dapat diterima dengan aman oleh skrip bersarang. Ada ide?

test.sh

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

myecho.sh

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

Sampel:

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

Hasil:

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

Hasil yang diinginkan

aaa bbb "ccc ddd"
aaa
bbb
ccc ddd
dinginkan
sumber
2
Saya baru saja akan menanyakan pertanyaan itu! Waktu yang tepat.
Scottie T

Jawaban:

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

Perhatikan bahwa konstruksi "$ @" tidak spesifik untuk bash dan harus bekerja dengan shell POSIX manapun (setidaknya dengan tanda hubung). Perhatikan juga bahwa mengingat keluaran yang Anda inginkan, Anda tidak memerlukan tingkat kutipan ekstra sama sekali. IE panggil saja skrip di atas seperti:

./test.sh 1 2 "3 4"
pixelbeat
sumber
5
"$ @" dapat digunakan dengan semua shell Bourne atau turunan Bourne shell (dari 1978 dan seterusnya), termasuk Korn dan Bash. Mungkin 95% dari waktu, menggunakan "$ @" adalah benar dan $ * salah.
Jonathan Leffler
Bagus! Tapi, apakah ada yang tahu jika ada cara untuk menyimpannya "sebagaimana adanya" dalam variabel? Asli $@tidak tersedia di dalam fungsi (karena ditimpa oleh argumen fungsi). Aku mencoba foovar="$@"dan foovar=$@+ "$foovar"dalam fungsi dan tidak bekerja: - /
bitifet
143

Anda ingin menggunakan "$ @" (kutipan dolar di) untuk meneruskan parameter ke subskrip. Seperti itu ....

ls-color.sh:

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


Mengapa .....

Dari halaman manual Bash :

$*- Memperluas ke parameter posisi, mulai dari satu. Ketika ekspansi terjadi dalam tanda kutip ganda, ekspansi itu meluas ke satu kata dengan nilai setiap parameter yang dipisahkan oleh karakter pertama dari variabel khusus IFS. Artinya, "$*"sama dengan "$1c$2c...", di mana c adalah karakter pertama dari nilai variabel IFS. Jika IFS tidak disetel, parameter dipisahkan oleh spasi. Jika IFS adalah null, parameter digabungkan tanpa pemisah intervensi.

$@- Memperluas ke parameter posisi, mulai dari satu. Saat perluasan terjadi dalam tanda kutip ganda, setiap parameter diperluas ke kata terpisah. Artinya, "$@"ekuivalen dengan "$1" "$2" ...Jika ekspansi bertanda kutip ganda terjadi dalam sebuah kata, perluasan parameter pertama digabungkan dengan bagian awal kata asli, dan perluasan parameter terakhir digabungkan dengan bagian terakhir dari kata asli. kata. Ketika tidak ada parameter posisi, "$@"dan $@berkembang menjadi tidak ada (yaitu, mereka dihapus).


Menyiapkan beberapa skrip demo ...

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

"$@"- quote-dollar-at adalah transformasi identitas untuk meneruskan argumen ke subkulit (~ 99% dari waktu, inilah yang ingin Anda lakukan):

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

"$*"- quote-dollar-star menghancurkan args menjadi satu string (~ 1% dari waktu Anda benar-benar menginginkan perilaku ini, misalnya dalam kondisi if [[ -z "$*" ]]; then ...:):

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

$*/ $@- tanpa tanda kutip, kedua bentuk menghapus satu tingkat kutipan dan menafsirkan spasi dari string yang mendasarinya tetapi mengabaikan karakter kutipan (hampir selalu, ini adalah kesalahan):

./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

Jika Anda ingin bersenang-senang, Anda dapat menggunakan "$ @" untuk mengumpulkan hal-hal sedalam yang Anda inginkan, mendorong dan mengeluarkan elemen dari tumpukan args jika Anda mau.

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
sumber
1
Terima kasih untuk penjelasannya. Baru saja menggunakan "$ *" untuk alias grep.
darkless
Anda menyelamatkan hari saya!
xyz