文字列の配列内に文字列が存在するかどうかをテストする

文字列の配列内に文字列が存在するかどうかをテストする

ファイル内の各行の最初の 3 文字 (cut を使用して取得) を配列内の文字列と比較するスクリプトを作成しています。すでに調べましたが、見つけた解決策は私のシステムでは機能しませんでした。

今のところは次のようになっています:

weekdays=([Mon]=1 [Tue]=1 [Wed]=1 [Thu]=1 [Fri]=1 [Sat]=1 [Sun]=1)
input="/Foo/Bar.log"

while read -r line
do

cutline="$(echo ${line} | cut -c 1-3"

if [[ ${weekdays["$cutline"]} ]]
then
echo "Match"
else
echo "No Match"
fi

done < ${input}

ラインは適切にカットされますが、最初の 3 文字が何であっても「一致」が返されるため、テスト中に何かが誤検知を返します。

-xでスクリプトをチェックすると、実際に使用されたテストの代わりに、

[[ -n 1 ]]

[ ]そして、それを式でテストすると、1

単語全体ではなく配列内のすべての文字をチェックしますか、それとも何か他の問題がありますか?

問題がなければ、次の行に進む前に、行の最初の 3 文字を配列内のすべてのものと比較する別の方法はありますか?

補足:私は実際にBash 4を実行しているので、連想配列は動作するはずです

答え1

基本的なエラーは、実際には連想配列を宣言していないことです。

$ weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
$ echo ${weekdays[@]}
1
$ echo ${weekdays[0]}
1
$ echo ${weekdays[2]}

$

bash がこれをどのように処理し、なぜ だけを取るのかはよく分かりません1が、それが連想配列ではないことは確かです。説明されているようにman bash(強調は筆者による):

構文 name[subscript]=value を使用して変数が割り当てられると、インデックス付き配列が自動的に作成されます。subscript は数値に評価される算術式として扱われます。インデックス付き配列を明示的に宣言するには、declare -a name を使用します (以下の SHELL BUILTIN COMMANDS を参照)。declare -a name[subscript] も受け入れられますが、subscript は無視されます。

連想配列は、declare -A name を使用して作成されます。

したがって、代わりにこれを試してみると、期待どおりに動作します。

declare -A weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)

とはいえ、このスクリプトは必要以上に複雑です。同じアプローチを使用した、よりシンプルなバージョンを次に示します。

#!/bin/bash
declare -A weekdays=(["Mon"]=1 ["Tue"]=1 ["Wed"]=1 ["Thu"]=1 ["Fri"]=1 ["Sat"]=1 ["Sun"]=1)
input="/Foo/Bar.log"

cut -c 1-3 "$input" | while read -r line; do
    if [[ ${weekdays["$line"]} ]]
    then
            echo "Match : $cutline : ${weekdays[$line]}"
    else
            echo "No Match"
    fi
done    

ただし、私はおそらく次のようにします:

#!/bin/bash
cut -c 1-3 "$1" | while read -r line; do
        case $line in
        "Mon"|"Tue"|"Wed"|"Thu"|"Fri"|"Sat"|"Sun")
                        echo yes;;
                *)
                        echo no;;
        esac
done

次に、ターゲット ファイル名を引数としてスクリプトを実行します。

script.sh /Foo/Bar.log"

答え2

テキスト処理ツールを1回呼び出してテキストを処理します入力行ごとに複数のツールを使用するのではなく、

awk -v 'weekday=(Mon|Tue|Wed|Thu|Fri|Sat|Sun)' '
  {print ($0 ~ "^" weekday ? "" : "No ") "Match"}' < "$input"

入力の各行に対して特定のアプリケーションを実行する必要がある場合はループを使用しますが、行をファイルに出力するなどのテキスト処理だけの場合は、awk次のように実行できます。

awk -v 'weekday=Mon|Tue|Wed|Thu|Fri|Sat|Sun' '
  (day = substr($0, 1, 3)) ~ weekday {
    print substr($0, 4) > day ".txt"
  } < "$input"

関連情報