grepコマンドで文字列検索をifステートメントに入れる方法は?


11

2つのファイルで複数の文字列を検索したい。両方のファイルで1つの文字列が見つかった場合は、何かを作成します。1つの文字列が1つのファイルだけで見つかった場合は、別の文字列を作成します。

私のコマンドは次のとおりです:

####This is for the affirmative sentence in both files
if grep -qw "$users" "$file1" && grep -qw "$users" "$file2"; then

####This is for the affirmative sentence in only one file, and negative for the other one
if grep -qw "$users" "$file1" ! grep -qw "$users" "$file2"; then

それは声明を否定し、肯定する正しい方法ですか?pd KSHシェルを使用しています。

前もって感謝します。

回答:


11

これを試して:

if grep -wq -- "$user" "$file1" && grep -wq -- "$user" "$file2" ; then
   echo "string avail in both files"
elif grep -wq -- "$user" "$file1" "$file2"; then
   echo "string avail in only one file"
fi
  • grep 複数のファイルでパターンを検索できるため、OR / NOT演算子を使用する必要はありません。

各コマンドで「-wq」と「-qw」の間に最小限の違いはありますか?
Mareyes

2
いいえ、どちらもgrepに-qおよび-wオプションを提供します。
グレンジャックマン

3
なぜ変数展開に引用符がないのですか?
D.ベンノーブル

@ D.BenKnobleは正しいですか: "$ user" "$ file1"?
-Mareyes

1
@Mareyesはいそれは正しいです
D.ベンKnoble

13

別のオプション:

grep -qw -- "$users" "$file1"; in_file1=$?
grep -qw -- "$users" "$file2"; in_file2=$?

case "${in_file1},${in_file2}" in
    0,0) echo found in both files ;;
    0,*) echo only in file1 ;;
    *,0) echo only in file2 ;;
      *) echo in neither file ;;
esac

オプションの方法をご利用いただき、ありがとうございます。それは十分にうまくいきます!! 私が持っている別のスクリプトで完全に動作します。
Mareyes 2018

いい考えだ!いつも学ぶ。
ジョー

7
n=0

#Or if you have more files to check, you can put your while here. 
grep -qw -- "$users" "$file1" && ((n++))
grep -qw -- "$users" "$file2" && ((n++))

case $n in 
   1) 
       echo "Only one file with the string"
    ;;
   2)
       echo "The two files are with the string"
   ;;
   0)
       echo "No one file with the string"
   ;;
   *)
       echo "Strange..."
   ;;
esac 

注:((n++))はksh拡張機能です(zshおよびでもサポートされていますbash)。POSIX sh構文では、n=$((n + 1))代わりに必要になります。


((n ++))の代わりに$((n ++))と入力してしまい申し訳ありません。忘れる。
Luciano Andress Martini

1
ポストインクリメントであるn=$((n++))ため、nの値を変更しないことに注意してください。n の現在の値を返し、nをインクリメントします。次に、元のnであった戻り値に変数を設定します。n++
グレンジャックマン

ファイル名に改行が含まれていないと想定できる場合はlocal IFS=$'\n'; matches=( $(grep -lw "$users" "$file1" "$file2") )、を使用しますcase "${#matches[@]" in。@glenn:この考え方はあなたの答えにも当てはまり、の複数の呼び出しを回避しますgrep。それを配管してgrep -l | wc -lも動作します。
ピーター

@Peterは、別の回答IMOとして書く価値があります。
スティーブンキット

@StephenKitt:任意のファイル名文字を処理する完全な方法を見つけることができなかったので、私は行きませんでした。しかし、あなたはそれが価値があると思うので、とにかくそれを書きました。
ピーター

2

ファイル名に改行が含まれていない場合は、grepgrepで一致するファイルの名前を出力し、結果をカウントすることで、複数回の呼び出しを回避できます。

 local IFS=$'\n'    # inside a function.  Otherwise use some other way to save/restore IFS
 matches=( $(grep -lw "$users" "$file1" "$file2") )

一致数は "${#matches[@]}"

grep --null -lwここで使用する方法があるかもしれませんが、出力を解析する方法がわかりません。Bashにvar=( array elements )は、の\0代わりに区切り文字を使用する方法がありません\n。たぶんbashのmapfileビルトインはそれを行うことができますか?しかし、おそらくそうではありません。区切り文字を-d string。。


可能ですがcount=$(grep -l | wc -l)、2つの外部プロセスがあるためgrep、2つのファイルを個別に実行することもできます。(grepvs.wc起動オーバーヘッドは全く別のプロセスを起動するフォーク+幹部+動的リンカーのものと比較して小さいです)。

また、wc -lあなたが一致するファイルを見つけることはできません。


結果が配列にキャプチャされているので、それが既に望んでいるものである可能性があります。または、正確に1つの一致がある場合、それが最初の入力であるかどうかを確認できます。

local IFS=$'\n'    # inside a function.  Otherwise use some other way to save/restore IFS
matches=( $(grep -lw "$users" "$file1" "$file2") )

# print the matching filenames
[[ -n $matches ]] && printf  'match in %s\n'  "${matches[@]}"

# figure out which input position the name came from, if there's exactly 1.
if [[ "${#matches[@]" -eq 1 ]]; then
    if [[ $matches == "$file1" ]];then
        echo "match in file1"
    else
        echo "match in file2"
    fi
fi

$matches${matches[0]}、最初の配列要素の省略形です。


@StephenKitt:おっと-z、ではなく-0、という意味でした。はい、それはgrepが\0答えですでに述べたように区切られたファイル名を印刷するようになりますが、どのようにbashを取得してそれを解析できますか?を設定することはできませんIFS=$'\0'。または、基本的にfind -print0、bashで直接スタイル出力を使用して何かを行うことはできません。
Peter Cordes

はい、読むのが速すぎて、その段落が欠落していて、物事をじっくり考えていませんでした(そのため、コメントを削除しました)。方法はあるかもしれませんが、今は考えられません。あなたが言うようにgrep、とにかく対処するのがほんの数個しかない場合、ソリューションをあまり複雑にする価値はありません。
スティーブンキット2018

1
mapfile -d $'\0'一種の作品ですが、改行をスペースに置き換えます(私は調整を試していませんIFS)。
Stephen Kitt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.