シェル:ifでパラメーター付きの関数を使用する


8

以下のコードを実行しようとしていますが、ifステートメントで関数を使用しようとすると、-bash: [: too many argumentsエラーが発生します。なぜそれが起こっているのですか?

前もって感謝します!

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

1
使用shellcheck.net(またはそのオフライン版)。それははるかに詳細な説明はなく、解決策はありませんが、回答で言及されている問題を見つけます。
David Foerster、2018

回答:


14

使用if [ ... ]するときは、実際に[ユーティリティを使用しています(これはと同じtestですが、最後の引数がである必要があります])。

[関数の実行を理解できません。文字列が必要です。幸い、[ここでは(少なくとも関数に対して)を使用する必要はありません。

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

また、最初に整数をチェックしているので、$docheck1でない場合は関数をまったく呼び出さないようにすることができます。

これifは、任意のコマンドを受け取り、そのコマンドの終了ステータスから何をするかを決定するため機能します。ここでは、[ ... ]テストと関数の呼び出しを併用し、&&その間に複合コマンドを作成します。[ ... ]テストと関数の両方が終了ステータスとしてゼロを返し、成功を通知した場合、複合コマンドの終了ステータスはtrueになります。

スタイルのメモとして、配列に要素が含まれていないかどうかをテストしませが、要素が含まれているかどうかをテストます。

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

関数テストでネガティブを使用すると、配列に要素()が含まれているかどうかをテストする必要ある場合、ロジックが台無しになりますif ! notContainsElement ...


回答やその他のアドバイスをありがとうございました!
Andrea Silvestri

3

試す

if notContainsElement "$fruit" "${blacklist[@]}" && test "$docheck" = 1
then
  • -aオプションは、シェルまたはでもないtest選択肢

ここで2つの部分のテストがあります

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

あなたがif使用してリンクします

if cmd1 && cmd2

指摘したよう-aに、テストオプションですが、他のテストオプションでのみ使用できるため、

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

テストの$a両方よりも低い$b$c、しかし、あなたがテスト範囲内で他のコマンドを使用することはできません。


arg、@ Kusalanandaは再び世界の野生のウェブで最速の銃でした;)
Archemar

RSIはそのために支払う価格です。
クサラナンダ

-aあるテストオプションは、それが意味し。
ハイド

@hydeただし、POSIX標準では廃止予定としてマークされています。
クサラナンダ

1
@Archemar、...あなたの例にまだ残っている引用バグの修正を検討してください(引用されていない$docheck、引用されていない$a/ $b/最後の例では/ etc)。
チャールズダフィー

0

これは一部の人々が好まないかもしれない代替案です。ブラックリスト配列を文字列に変換し、フルーツが削除された文字列が同じかどうかを確認します。文字列にスペースを埋め込むように編集されました。アップル/パイナップル問題を指摘してくれたスコットに感謝します。

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

これはもっと簡単だと思いますが、多くの人が好む&&ロジックが欠けています。


(1)list果物の1つが果物の1つに含まれている場合、これは失敗しblacklistます。たとえば、に変更blacklistしても( "oranges" "pineapples" )、スクリプトの出力は変更されません。…(続き)
スコット

(続き)…(2)「私はこれはより単純だと思いますが、多くの人が好む&&ロジックが欠けている」という意味がよくわかりません。多くの場合、機能を省くことにより、物事をより単純にする方が簡単です。アルバート・アインシュタインがか言っていない可能性があり、「すべてはできるだけシンプルなされていないが、ないより単純されなければなりません。」なぜ除外したの&&ですか?(3)コードを投稿する際は、適宜字下げしてください。
スコット

さて、4スペースインデントを使用しようとしましたが、機能しなかったようです。でも、パイナップルについてどういう意味かわかります。
Wastrel

また、単一のリスト項目を区別できないtwo words、と2つの以降の項目、twoおよびwords。リストが*エントリとして含まれている場合、それはすべてに一致します。(あなたが引用されていないため、そして、$fどうあるべきかでecho "$f"あなたがあれば、なかったような要素をエコーしようとすると、それが現在のディレクトリ内のファイル名のリストに置き換えられます)。
チャールズダフィー

いくつかの編集を行いました。配列内のデータが任意の場合、これは簡単なことではありません。「apples」がブラックリストにあり、「Granny Smith apples」が他の配列の要素であるとします。OPのコードにも同様の問題があると思います。配列は、「機能する」要素で構成する必要があります。
Wastrel
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.