Ketika mereka tidak dikutip, $*
dan $@
sama. Anda tidak boleh menggunakan salah satu dari ini, karena mereka dapat pecah secara tiba-tiba begitu Anda memiliki argumen yang berisi spasi atau wildcard.
"$*"
meluas ke satu kata "$1c$2c..."
. Biasanya c
adalah spasi, tetapi sebenarnya adalah karakter pertama IFS
, jadi itu bisa apa saja yang Anda pilih.
Satu-satunya penggunaan yang baik yang pernah saya temukan untuk itu adalah:
gabungkan argumen dengan koma (versi sederhana)
join1() {
typeset IFS=,
echo "$*"
}
join1 a b c # => a,b,c
bergabung dengan argumen dengan pembatas yang ditentukan (versi yang lebih baik)
join2() {
typeset IFS=$1 # typeset makes a local variable in ksh (see footnote)
shift
echo "$*"
}
join2 + a b c # => a+b+c
"$@"
memperluas kata-kata yang terpisah: "$1"
"$2"
...
Ini hampir selalu yang Anda inginkan. Itu memperluas setiap parameter posisi ke kata yang terpisah, yang membuatnya sempurna untuk mengambil argumen baris perintah atau fungsi dan kemudian meneruskannya ke perintah atau fungsi lain. Dan karena diperluas menggunakan tanda kutip ganda, itu berarti hal-hal tidak rusak jika, katakanlah, "$1"
berisi spasi atau tanda bintang ( *
).
Mari kita menulis skrip yang disebut svim
berjalan vim
dengan sudo
. Kami akan melakukan tiga versi untuk menggambarkan perbedaannya.
svim1
#!/bin/sh
sudo vim $*
svim2
#!/bin/sh
sudo vim "$*"
svim3
#!/bin/sh
sudo vim "$@"
Semuanya akan baik-baik saja untuk kasus-kasus sederhana, misalnya satu nama file yang tidak mengandung spasi:
svim1 foo.txt # == sudo vim foo.txt
svim2 foo.txt # == sudo vim "foo.txt"
svim2 foo.txt # == sudo vim "foo.txt"
Tetapi hanya $*
dan "$@"
bekerja dengan baik jika Anda memiliki banyak argumen.
svim1 foo.txt bar.txt # == sudo vim foo.txt bar.txt
svim2 foo.txt bar.txt # == sudo vim "foo.txt bar.txt" # one file name!
svim3 foo.txt bar.txt # == sudo vim "foo.txt" "bar.txt"
Dan hanya "$*"
dan "$@"
berfungsi dengan baik jika Anda memiliki argumen yang berisi spasi.
svim1 "shopping list.txt" # == sudo vim shopping list.txt # two file names!
svim2 "shopping list.txt" # == sudo vim "shopping list.txt"
svim3 "shopping list.txt" # == sudo vim "shopping list.txt"
Jadi hanya "$@"
akan berfungsi dengan baik sepanjang waktu.
typeset
adalah cara membuat variabel lokal di ksh
( bash
dan ash
menggunakan local
sebagai gantinya). Itu berarti IFS
akan dikembalikan ke nilai sebelumnya ketika fungsi kembali. Ini penting, karena perintah yang Anda jalankan sesudahnya mungkin tidak berfungsi dengan baik jika IFS
disetel ke sesuatu yang tidak standar.
$*
. Saya selalu menganggap itu sama sekali tidak berguna ... bergabung dengan pembatas adalah kasus penggunaan yang baik.Jawaban singkat: gunakan
"$@"
(perhatikan tanda kutip ganda). Bentuk-bentuk lain sangat jarang berguna."$@"
adalah sintaks yang agak aneh. Itu digantikan oleh semua parameter posisi, sebagai bidang yang terpisah. Jika tidak ada parameter posisional ($#
0), maka"$@"
ekspansi menjadi nol (bukan string kosong, tetapi daftar dengan 0 elemen), jika ada satu parameter posisional maka"$@"
sama dengan"$1"
, jika ada dua parameter posisional maka"$@"
setara dengan"$1" "$2"
, dll."$@"
memungkinkan Anda untuk meneruskan argumen skrip atau fungsi ke perintah lain. Ini sangat berguna untuk pembungkus yang melakukan hal-hal seperti mengatur variabel lingkungan, menyiapkan file data, dll. Sebelum memanggil perintah dengan argumen dan opsi yang sama dengan pembungkus dipanggil.Misalnya, fungsi berikut memfilter output dari
cvs -nq update
. Terlepas dari pemfilteran output dan status pengembalian (yanggrep
bukan dari itucvs
), memanggilcvssm
beberapa argumen berperilaku seperti memanggilcvs -nq update
dengan argumen ini."$@"
memperluas ke daftar parameter posisi. Dalam shell yang mendukung array, ada sintaksis yang sama untuk memperluas ke daftar elemen array:"${array[@]}"
(kawat gigi wajib diisi kecuali di zsh). Sekali lagi, tanda kutip ganda agak menyesatkan: mereka melindungi terhadap pemisahan bidang dan pembuatan pola elemen array, tetapi setiap elemen array berakhir di bidangnya sendiri.Beberapa cangkang kuno memiliki apa yang bisa dibilang bug: ketika tidak ada argumen posisional,
"$@"
diperluas ke bidang tunggal yang berisi string kosong, bukannya ke bidang tidak ada. Ini mengarah pada solusi${1+"$@"}
(dibuat terkenal melalui dokumentasi Perl ). Hanya versi yang lebih lama dari shell Bourne aktual dan implementasi OSF1 yang terpengaruh, tidak ada penggantian yang kompatibel modern (abu, ksh, bash, ...)./bin/sh
tidak terpengaruh pada sistem apa pun yang dirilis pada abad ke-21 yang saya tahu (kecuali jika Anda menghitung rilis pemeliharaan Tru64, dan bahkan ada/usr/xpg4/bin/sh
yang aman sehingga hanya#!/bin/sh
skrip yang terpengaruh, bukan#!/usr/bin/env sh
skrip selama PATH Anda diatur untuk kepatuhan POSIX) . Singkatnya, ini adalah anekdot sejarah yang tidak perlu Anda khawatirkan."$*"
selalu mengembang menjadi satu kata. Kata ini berisi parameter posisi, disatukan dengan spasi di antaranya. (Lebih umum, pemisah adalah karakter pertama dari nilaiIFS
variabel. Jika nilaiIFS
adalah string kosong, pemisah adalah string kosong.) Jika tidak ada parameter posisi maka"$*"
string kosong, jika ada dua parameter posisi danIFS
memiliki nilai standarnya kemudian"$*"
setara dengan"$1 $2"
, dll.$@
dan$*
kutipan luar sama. Mereka memperluas ke daftar parameter posisi, sebagai bidang yang terpisah, seperti"$@"
; tetapi setiap bidang yang dihasilkan kemudian dibagi menjadi bidang-bidang terpisah yang diperlakukan sebagai pola wildcard nama file, seperti biasa dengan ekspansi variabel yang tidak dikutip.Misalnya, jika direktori saat ini berisi tiga file
bar
,baz
danfoo
, maka:sumber
"$@"
memang berkembang ke daftar yang berisi string kosong: unix.stackexchange.com/questions/68484/…Berikut ini adalah skrip sederhana untuk menunjukkan perbedaan antara
$*
dan$@
:Keluaran:
Dalam sintaks array, tidak ada perbedaan saat menggunakan
$*
atau$@
. Ini hanya masuk akal ketika Anda menggunakannya dengan tanda kutip ganda"$*"
dan"$@"
.sumber
IFS="^${IFS}"
?IFS
.IFS="^xxxxx"
akan dilakukan?${IFS}
Akhiran trailing membuat saya berpikir Anda melakukan sesuatu yang lebih rumit, seperti entah bagaimana secara otomatis memulihkan IFS asli di akhir (misalnya: char pertama secara otomatis digeser keluar atau sesuatu).Kode yang Anda berikan akan memberikan hasil yang sama. Untuk memahaminya dengan lebih baik, coba ini:
Outputnya sekarang harus berbeda. Inilah yang saya dapatkan:
Ini berhasil bagi saya
bash
. Sejauh yang saya tahu, ksh seharusnya tidak jauh berbeda. Pada dasarnya, mengutip$*
akan memperlakukan semuanya sebagai satu kata, dan mengutip$@
akan memperlakukan daftar sebagai kata-kata yang terpisah, seperti yang dapat dilihat pada contoh di atas.Sebagai contoh menggunakan
IFS
variabel dengan$*
, pertimbangkan iniSaya mendapatkan ini sebagai hasilnya:
Juga, saya baru saja mengkonfirmasi itu berfungsi sama
ksh
. Keduanyabash
danksh
diuji di sini di bawah OSX tapi saya tidak bisa melihat bagaimana itu akan berarti banyak.sumber
unset IFS
di akhir untuk meresetnya ke aslinya, tetapi itu berhasil bagi saya tidak ada masalah dan melakukan hasilecho $IFS
dalam output standar yang saya dapatkan dari itu. MengaturIFS
withing kurung keriting memperkenalkan ruang lingkup baru, jadi kecuali Anda mengekspornya, itu tidak akan mempengaruhi luarIFS
.echo $IFS
tidak membuktikan apa-apa, karena shell melihat,
, tetapi kemudian kata splitting menggunakanIFS
! Cobaecho "$IFS"
.IFS
harus menyelesaikannya.IFS
memiliki nilai khusus yang berbeda sebelum memanggil fungsi. Tapi ya, sebagian besar waktu yang tidak disetel IFS akan berfungsi.Perbedaannya penting ketika menulis skrip yang harus menggunakan parameter posisi dengan cara yang benar ...
Bayangkan panggilan berikut:
Di sini hanya ada 4 parameter:
Dalam kasus saya,
myuseradd
hanya pembungkus untukuseradd
yang menerima parameter yang sama, tetapi menambahkan kuota untuk pengguna:Perhatikan panggilan untuk
useradd "$@"
, dengan$@
kutipan. Ini akan menghormati parameter dan mengirimkannya apa adanyauseradd
. Jika Anda membatalkan tanda kutip$@
(atau menggunakan$*
juga tanda kutip ), useradd akan melihat 5 parameter, karena parameter ke-3 yang berisi spasi akan dibagi menjadi dua:(dan sebaliknya, jika Anda menggunakan
"$*"
, useradd akan hanya melihat satu parameter:-m -c Carlos Campderrós ccampderros
)Jadi, singkatnya, jika Anda perlu bekerja dengan parameter yang menghormati parameter multi-kata, gunakan
"$@"
.sumber
// bash bash . adalah ksh, affair, perilaku serupa.
sumber
Berbicara tentang perbedaan antara
zsh
danbash
:Dengan kutipan di sekitar
$@
dan$*
,zsh
danbash
berperilaku sama, dan saya kira hasilnya cukup standar di antara semua shell:Tanpa kutipan, hasilnya sama untuk
$*
dan$@
, tetapi berbeda di dalambash
dan dizsh
. Dalam hal inizsh
menunjukkan beberapa perilaku aneh:(Zsh biasanya tidak membagi data tekstual menggunakan IFS, kecuali diminta secara eksplisit, tetapi perhatikan bahwa di sini argumen kosong secara tak terduga hilang dalam daftar.)
sumber
$@
tidak khusus dalam hal ini:$x
meluas hingga paling banyak satu kata, tetapi variabel kosong tidak berkembang menjadi apa-apa (bukan kata kosong). Cobaprint -l a $foo b
denganfoo
kosong atau tidak terdefinisi.Salah satu jawaban mengatakan
$*
(yang saya anggap sebagai "percikan") jarang berguna.Saya mencari di Google dengan
G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }
Karena URL sering terpecah dengan
+
, tetapi keyboard saya membuatnyalebih mudah dijangkau daripada
+
,$*
+$IFS
merasa berharga.sumber