Shell: Menggunakan fungsi dengan parameter jika

8

Saya mencoba untuk mengeksekusi kode di bawah ini tetapi ketika saya mencoba untuk menggunakan fungsi saya di pernyataan if saya mendapatkan -bash: [: too many argumentskesalahan. Mengapa ini terjadi?

Terima kasih sebelumnya!

notContainsElement () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 1; done
  return 0
}

list=( "pears" "apples" "bananas" "oranges" )
blacklist=( "oranges" "apples" )
docheck=1

for fruit in "${list[@]}"
do
    if [ notContainsElement "$fruit" "${blacklist[@]}" -a $docheck = 1 ]
    then
        echo $fruit
    fi
done
Andrea Silvestri
sumber
1
Gunakan shellcheck.net (atau versi offline-nya). Ia menemukan masalah yang disebutkan dalam jawaban meskipun dengan penjelasan yang jauh lebih rinci dan tanpa solusi.
David Foerster

Jawaban:

14

Saat menggunakan if [ ... ]Anda sebenarnya menggunakan [utilitas (yang sama dengan testtetapi mengharuskan argumen terakhir ]).

[tidak mengerti untuk menjalankan fungsi Anda, ia mengharapkan string. Untungnya, Anda tidak perlu menggunakan [sama sekali di sini (setidaknya untuk fungsi):

if [ "$docheck" -eq 1 ] && notContainsElement "$fruit" "${blacklist[@]}"; then
  ...
fi

Perhatikan bahwa saya juga memeriksa integer terlebih dahulu, sehingga kita dapat menghindari memanggil fungsi sama sekali jika $docheckbukan 1.

Ini berfungsi karena ifmengambil perintah sewenang-wenang dan memutuskan apa yang harus dilakukan dari status keluar dari perintah itu. Di sini kami menggunakan [ ... ]tes bersama dengan panggilan ke fungsi Anda, dengan &&di antaranya, membuat perintah majemuk. Status keluar perintah majemuk akan benar jika [ ... ]tes dan fungsi mengembalikan nol sebagai status keluarnya, menandakan keberhasilan.

Sebagai catatan gaya, saya tidak akan memiliki tes fungsi apakah array tidak mengandung elemen tetapi apakah jika memang mengandung elemen, dan kemudian

if [ "$docheck" -eq 1 ] && ! contains "$fruit" "${blacklist[@]}"; then ...

Memiliki tes fungsi akan mengacaukan negatif up logika dalam kasus di mana Anda lakukan ingin menguji apakah array berisi elemen ( if ! notContainsElement ...).

Kusalananda
sumber
Terima kasih banyak atas jawabannya dan saran lainnya!
Andrea Silvestri
3

mencoba

if notContainsElement "$fruit" "${blacklist[@]}" && test "$docheck" = 1
then
  • -aopsi bukanlah shell atau testopsi

di sini Anda memiliki dua bagian tes

notContainsElement "$fruit" "${blacklist[@]}"
test $docheck = 1 ## or [ $docheck = 1 ]

Anda menautkan kemudian ifmenggunakan

if cmd1 && cmd2

seperti yang ditunjukkan -aadalah opsi tes, tetapi hanya dapat digunakan dengan opsi tes lainnya, sehingga Anda dapat menggunakannya

if [ "$a" -lt "$b" -a "$a" -lt "$c" ]

untuk menguji yang $alebih rendah dari keduanya $bdan $c, tetapi Anda tidak dapat menggunakan perintah lain dalam cakupan pengujian.

Archemar
sumber
arg, @Kusalananda adalah senjata tercepat di dunia web liar lagi;)
Archemar
RSI adalah harga yang saya bayar untuk itu.
Kusalananda
-aadalah opsi tes , artinya AND.
hyde
@hyde Kecuali sudah dianggap usang dalam standar POSIX.
Kusalananda
1
@Archemar, ... mempertimbangkan memperbaiki bug mengutip masih hadir dalam contoh Anda (kuotasi $docheck, kuotasi $a/ $b/ etc dalam contoh terakhir).
Charles Duffy
0

Berikut adalah alternatif yang mungkin tidak disukai sebagian orang. Konversikan daftar hitam Anda menjadi string, dan lihat apakah string dengan buah yang dihapus sama. Diedit untuk membuat string dengan spasi. Terima kasih kepada Scott karena telah menunjukkan masalah apel / nanas.

badlist=" ${blacklist[@]} "
for f in "${list[@]}"
do
    if [[ "${badlist/" $f "/}" == "$badlist" ]]
    then
        echo "$f"
    fi
done

Saya pikir ini lebih sederhana, tetapi tidak memiliki logika && yang disukai banyak orang.

Pemboros
sumber
(1) Ini gagal jika salah satu listbuah terkandung dalam salah satu blacklistbuah. Sebagai contoh, jika kita mengubah blacklistke ( "oranges" "pineapples" ), output dari script Anda tidak berubah. … (Lanjutan)
Scott
(Lanjutkan) ... (2) Saya tidak begitu mengerti apa yang Anda maksud dengan "Saya pikir ini lebih sederhana, tetapi tidak memiliki logika && yang lebih disukai banyak orang." Seringkali lebih mudah untuk membuat hal-hal lebih sederhana dengan meninggalkan fungsionalitas. Seperti yang mungkin atau tidak mungkin dikatakan oleh Albert Einstein, "Segala sesuatu harus dibuat sesederhana mungkin, tetapi tidak lebih sederhana." Mengapa Anda meninggalkan &&? (3) Ketika Anda memposting kode, silakan indentasi dengan tepat.
Scott
Yah, saya mencoba menggunakan indentasi 4-ruang tetapi sepertinya itu tidak berhasil. Tapi aku mengerti maksudmu tentang nanas.
Wastrel
Itu juga tidak dapat membedakan antara entri daftar tunggal two words, dan dua item berikutnya, twodan words. Dan jika daftar Anda berisi *entri, itu akan cocok dengan segalanya. (Dan karena Anda tidak mengutip $fdalam apa yang harus echo "$f", jika Anda tidak mencoba untuk menggemakan elemen seperti itu, itu akan diganti dengan daftar nama file dalam direktori saat ini).
Charles Duffy
Saya telah mengedit. Ini tidak sepele ketika data dalam array sewenang-wenang. Misalkan "apel" ada dalam daftar hitam dan "apel Nenek Smith" adalah elemen dari array lainnya. Saya pikir kode OP memiliki masalah serupa. Array harus dibuat dari elemen yang "berfungsi".
Wastrel