Saya sedang menulis skrip bash yang memiliki set -u
, dan saya memiliki masalah dengan ekspansi array kosong: bash tampaknya memperlakukan array kosong sebagai variabel yang tidak disetel selama ekspansi:
$ set -u
$ arr=()
$ echo "foo: '${arr[@]}'"
bash: arr[@]: unbound variable
( declare -a arr
tidak membantu.)
Solusi umum untuk ini adalah dengan menggunakan ${arr[@]-}
sebagai gantinya, dengan demikian menggantikan string kosong sebagai ganti array kosong ("tidak ditentukan"). Namun ini bukan solusi yang baik, karena sekarang Anda tidak dapat membedakan antara sebuah array dengan satu string kosong di dalamnya dan sebuah array kosong. (@ -expansion istimewa dalam bash, ia berkembang "${arr[@]}"
menjadi "${arr[0]}" "${arr[1]}" …
, yang menjadikannya alat yang sempurna untuk membuat baris perintah.)
$ countArgs() { echo $#; }
$ countArgs a b c
3
$ countArgs
0
$ countArgs ""
1
$ brr=("")
$ countArgs "${brr[@]}"
1
$ countArgs "${arr[@]-}"
1
$ countArgs "${arr[@]}"
bash: arr[@]: unbound variable
$ set +u
$ countArgs "${arr[@]}"
0
Jadi apakah ada cara untuk mengatasi masalah itu, selain memeriksa panjang array dalam if
(lihat contoh kode di bawah), atau mematikan -u
pengaturan untuk potongan pendek itu?
if [ "${#arr[@]}" = 0 ]; then
veryLongCommandLine
else
veryLongCommandLine "${arr[@]}"
fi
Pembaruan:bugs
Tag dihapus karena penjelasan oleh ikegami.
"${arr[@]}"
. Apakah saya melewatkan sesuatu? Dari apa yang saya lihat setidaknya berhasil5.x
.Menurut dokumentasi,
Tidak ada subskrip yang diberi nilai, jadi lariknya tidak disetel.
Tetapi sementara dokumentasi menyarankan kesalahan sesuai di sini, ini tidak lagi menjadi masalah sejak 4.4 .
Ada syarat yang dapat Anda gunakan sebaris untuk mencapai apa yang Anda inginkan di versi yang lebih lama: Gunakan
${arr[@]+"${arr[@]}"}
sebagai ganti"${arr[@]}"
.Diuji dengan bash 4.2.25 dan 4.3.11.
sumber
[@]+
sebenarnya terjadi dan mengapa yang kedua${arr[@]}
tidak menyebabkan kesalahan tak terikat.${parameter+word}
hanya meluasword
jikaparameter
tidak disetel.${arr+"${arr[@]}"}
lebih pendek dan tampaknya berfungsi dengan baik.unset arr
,arr[1]=a
,args ${arr+"${arr[@]}"}
Vsargs ${arr[@]+"${arr[@]}"}
+
ekspansi tidak terjadi (yaitu, array kosong), ekspansi diganti dengan apa - apa , yang merupakan perluasan dari array kosong.:+
tidak aman karena juga memperlakukan('')
larik elemen tunggal sebagai tidak disetel dan juga meluas menjadi tidak ada, kehilangan nilainya.Jawaban yang diterima @ ikegami agak salah! Mantra yang benar adalah
${arr[@]+"${arr[@]}"}
:sumber
bash-4.4.23
:arr=('') && countArgs "${arr[@]:+${arr[@]}}"
menghasilkan1
. Tetapi${arr[@]+"${arr[@]}"}
bentuk memungkinkan untuk membedakan antara nilai kosong / tidak kosong dengan menambahkan / tidak menambahkan titik dua.arr=('') && countArgs ${arr[@]:+"${arr[@]}"}
->0
,arr=('') && countArgs ${arr[@]+"${arr[@]}"}
->1
.Ternyata penanganan array telah diubah dalam rilis baru-baru ini (2016/09/16) bash 4.4 (tersedia di bentangan Debian, misalnya).
Sekarang ekspansi array kosong tidak mengeluarkan peringatan
sumber
bash-4.4.12
"${arr[@]}"
sudah cukup.ini mungkin opsi lain bagi mereka yang memilih untuk tidak menduplikasi arr [@] dan boleh saja memiliki string kosong
untuk menguji:
sumber
for
ini akan berakhir dengan satu string kosong ketika array tidak ditentukan / ditentukan-sebagai-kosong, di mana Anda mungkin menginginkan badan perulangan untuk tidak berjalan jika array tidak ditentukan.${arr[@]+"${arr[@]}"}
,, dengan benar mempertahankan status array kosong.Jawaban @ ikegami benar, tetapi saya menganggap sintaksnya
${arr[@]+"${arr[@]}"}
mengerikan. Jika Anda menggunakan nama variabel array yang panjang, itu mulai terlihat spaghetti-ish lebih cepat dari biasanya.Coba ini sebagai gantinya:
Sepertinya operator irisan array Bash sangat pemaaf.
Jadi mengapa Bash membuat penanganan kasus tepi array begitu sulit? Mendesah. Saya tidak dapat menjamin versi Anda akan mengizinkan penyalahgunaan operator irisan array, tetapi berfungsi dengan baik untuk saya.
Peringatan: Saya menggunakan
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
jarak tempuh Anda mungkin berbeda-beda.sumber
"${arr[@]:0}"
memberi-bash: arr[@]: unbound variable
.arr=("_dummy_")
, dan menggunakan perluasan di${arr[@]:1}
mana saja. Ini disebutkan dalam jawaban lain, mengacu pada nilai sentinel.Inkonsistensi yang "menarik" memang.
Selanjutnya,
Meskipun saya setuju bahwa perilaku saat ini mungkin bukan bug dalam arti yang dijelaskan @ikegami, IMO kami dapat mengatakan bug ada dalam definisi (dari "set") itu sendiri, dan / atau fakta bahwa itu diterapkan secara tidak konsisten. Paragraf sebelumnya di halaman manual mengatakan
yang sepenuhnya konsisten dengan apa yang dikatakannya tentang perluasan parameter posisi di
"$@"
. Bukan berarti tidak ada ketidakkonsistenan lain dalam perilaku array dan parameter posisi ... tetapi bagi saya tidak ada petunjuk bahwa detail ini tidak konsisten di antara keduanya.Melanjutkan,
Jadi
arr[]
bukankah begitu tidak terikat sehingga kita tidak bisa mendapatkan hitungan elemennya (0), atau daftar (kosong) kuncinya? Bagi saya ini masuk akal, dan berguna - satu-satunya pencilan tampaknya menjadi${arr[@]}
(dan${arr[*]}
) ekspansi.sumber
Saya melengkapi jawaban @ ikegami (diterima) dan @ kevinarpe (juga bagus).
Anda dapat melakukannya
"${arr[@]:+${arr[@]}}"
untuk mengatasi masalah tersebut. Sisi kanan (yaitu, setelah:+
) memberikan ekspresi yang akan digunakan jika sisi kiri tidak ditentukan / null.Sintaksnya misterius. Perhatikan bahwa sisi kanan ekspresi akan mengalami perluasan parameter, jadi perhatian ekstra harus diberikan untuk mendapatkan kutipan yang konsisten.
Seperti yang disebutkan @kevinarpe, sintaks yang kurang misterius adalah menggunakan notasi irisan array
${arr[@]:0}
(pada versi Bash>= 4.4
), yang meluas ke semua parameter, mulai dari indeks 0. Ini juga tidak memerlukan banyak pengulangan. Perluasan ini berfungsi apa punset -u
, jadi Anda dapat menggunakannya setiap saat. Halaman manual mengatakan (di bawah Ekspansi Parameter ):Ini adalah contoh yang disediakan oleh @kevinarpe, dengan format alternatif untuk menempatkan keluaran sebagai bukti:
Perilaku ini bervariasi dengan versi Bash. Anda mungkin juga telah memperhatikan bahwa operator panjang
${#arr[@]}
akan selalu mengevaluasi ke0
untuk larik kosong, terlepas dariset -u
, tanpa menyebabkan 'kesalahan variabel tak terikat'.sumber
:0
idiom gagal di Bash 4.2, jadi ini bukan pendekatan yang aman. Lihat jawabanku .Berikut adalah beberapa cara untuk melakukan sesuatu seperti ini, satu menggunakan sentinel dan yang lainnya menggunakan tambahan bersyarat:
sumber
Inkonsistensi yang menarik; ini memungkinkan Anda menentukan sesuatu yang "tidak dianggap set" namun muncul di keluaran
declare -p
PEMBARUAN: seperti yang disebutkan orang lain, diperbaiki di 4.4 dirilis setelah jawaban ini diposting.
sumber
echo ${arr[@]}
(tetapi sebelum Bash 4.4 Anda masih akan melihat kesalahan).echo $arr[@]
sendiri, Anda akan melihat bahwa pesan kesalahan berbeda.Cara yang paling sederhana dan kompatibel tampaknya:
sumber