Bagaimana cara menempatkan pencarian string dengan perintah grep ke if statement?

11

Saya ingin mencari beberapa string dalam dua file. Jika satu string ditemukan di kedua file, maka buat sesuatu. Jika satu string ditemukan hanya dalam satu file, maka buatlah hal lain.

Perintah saya adalah yang berikut:

####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

apakah ini cara yang benar untuk menolak dan menegaskan pernyataan? pd saya menggunakan shell KSH.

Terima kasih sebelumnya.

Mareyes
sumber

Jawaban:

11

Coba ini:

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 dapat mencari pola dalam banyak file, jadi tidak perlu menggunakan operator OR / NOT.
msp9011
sumber
Apakah ada perbedaan minimal antara "-wq" dan "-qw" di setiap perintah?
Mareyes
2
Tidak, keduanya menyediakan opsi -q dan -w untuk di-grep.
glenn jackman
3
Mengapa tidak ada penawaran pada ekspansi variabel?
D. Ben Knoble
@ D.BenKnoble apakah ini benar: "$ user" "$ file1"?
Mareyes
1
@Mareyes ya itu benar
D. Ben Knoble
13

Pilihan lain:

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
glenn jackman
sumber
Hai, terima kasih atas cara opsionalnya. Ini cukup berhasil !! berfungsi dengan baik di skrip lain yang saya miliki.
Mareyes
Itu ide yang bagus! Selalu belajar.
Joe
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 

Catatan: ((n++))adalah ekstensi ksh (juga didukung oleh zshdan bash). Dalam shsintaks POSIX , Anda akan membutuhkannya n=$((n + 1)).

Luciano Andress Martini
sumber
Oh maaf saya mengetik $ ((n ++)) bukan ((n ++)). Lupa.
Luciano Andress Martini
1
Perhatikan bahwa n=$((n++))tidak pernah akan mengubah nilai n karena n++adalah posting kenaikan: ia mengembalikan nilai saat n, maka kenaikan n; maka Anda mengatur variabel ke nilai yang dikembalikan yang asli n.
glenn jackman
Jika Anda dapat menganggap nama file Anda tidak mengandung baris baru local IFS=$'\n'; matches=( $(grep -lw "$users" "$file1" "$file2") ),, dan gunakan case "${#matches[@]" in. @glenn: ide ini berlaku untuk jawaban Anda juga, untuk menghindari beberapa pemanggilan grep. Menyalurkannya grep -l | wc -ljuga akan berhasil.
Peter Cordes
@Peter yang layak ditulis sebagai jawaban terpisah IMO.
Stephen Kitt
@StephenKitt: Saya tidak akan melakukannya karena saya tidak bisa menemukan cara antipeluru untuk menangani karakter nama file yang sewenang-wenang. Tapi karena Anda pikir itu sepadan, saya tetap menulisnya.
Peter Cordes
2

Jika nama file Anda tidak mengandung baris baru, Anda dapat menghindari beberapa pemanggilan grepdengan meminta cetak nama file yang cocok, dan hitung hasilnya.

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

Jumlah kecocokan adalah "${#matches[@]}".

Mungkin ada cara untuk digunakan di grep --null -lwsini, tapi saya tidak yakin bagaimana mem-parsing output . Bash var=( array elements )tidak memiliki cara untuk menggunakan \0pembatas alih-alih \n. Mungkin mapfilebuilt bash bisa melakukannya? Tapi mungkin tidak, karena Anda menentukan pembatas dengan -d string.


Anda bisa count=$(grep -l | wc -l), tetapi kemudian Anda memiliki dua proses eksternal sehingga Anda mungkin juga menjalankan grepdua file secara terpisah. (Perbedaan antara overhead startup grepvs wckecil dibandingkan dengan fork + exec + hal-hal linker dinamis untuk memulai proses yang terpisah sama sekali).

Juga, dengan wc -lAnda tidak menemukan file mana yang cocok.


Dengan hasil yang ditangkap dalam sebuah array, itu mungkin sudah seperti yang Anda inginkan, atau jika ada 1 yang cocok, Anda dapat memeriksa apakah itu input pertama atau tidak.

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

$matchesadalah singkatan untuk ${matches[0]}, elemen array pertama.

Peter Cordes
sumber
@StephenKitt: Maksud saya -z, bukan -0, oops. Ya itu akan mendapatkan grep untuk mencetak nama file yang dipisahkan oleh \0seperti yang telah saya sebutkan dalam jawaban, tetapi bagaimana Anda bisa mendapatkan bash untuk menguraikan itu? Anda tidak dapat mengatur IFS=$'\0', atau pada dasarnya melakukan hal lain dengan find -print0keluaran gaya langsung dengan bash.
Peter Cordes
Ya, saya membaca terlalu cepat, melewatkan paragraf itu, dan tidak memikirkan semuanya (itulah sebabnya saya menghapus komentar saya). Mungkin ada cara, tapi saya tidak bisa memikirkannya sekarang, dan seperti yang Anda katakan itu tidak layak membuat solusi terlalu rumit ketika hanya ada beberapa grepyang harus diselesaikan.
Stephen Kitt
1
mapfile -d $'\0'semacam karya, tetapi menggantikan baris baru dengan spasi (saya belum mencoba tweaker IFS).
Stephen Kitt