Menggunakan perintah grep dengan array kata

Sep 10 2020

Saya punya file teks dengan beberapa baris dan menyimpan file teks ke dalam array menggunakan

readarray -t array < Textfile

File teks berisi misalnya berikut ini:

123
456
789

Sekarang saya ingin menggunakan perintah grep untuk mencari hasil di file teks lain menggunakan array, jadi cetaklah baris di mana "123", "456" atau "789" muncul. Untuk menguji array, saya mencoba membuat grep mencari "123", "456" dan "789" file teks yang sama dan mengeluarkan baris yang cocok, jika setidaknya salah satu dari tiga rangkaian karakter muncul.

Saya mencoba

grep "${array[*]}" Textfile

tetapi ini tidak menunjukkan hasil apa pun. Apa yang saya lakukan salah?

Jawaban

4 terdon Sep 10 2020 at 13:43

Pertama-tama, Anda bisa menggunakan file itu sendiri. Itu jauh lebih sederhana daripada mencoba menggunakan array shell:

grep -f file1.txt file2.txt

Itu akan mencetak baris apa pun dari file2.txtyang cocok dengan baris mana pun file1.txt.

Jika Anda harus menggunakan array karena alasan tertentu, segalanya menjadi lebih rumit. Anda tidak bisa begitu saja grep "${array[*]}" Textfilekarena "${array[*]}"akan memperluas ke daftar elemen dalam larik yang dipisahkan oleh spasi:

$ array=("foo" "bar" "baz") $ echo "${array[*]}"
foo bar baz

Artinya, grepperintah Anda akan menjadi:

grep 'foo bar baz' file

Dan itu berarti "grep untuk foo bar bazdalam file file". Yang ingin Anda lakukan adalah grep untuk foo, atau bar , atau baz . Ini dapat dilakukan dengan menggunakan opsi -E grepdan menggabungkan pola yang ingin Anda cari |:

grep -E 'foo|bar|baz' file

Untuk melakukan ini, Anda perlu melakukan sesuatu yang rumit seperti:

grep -E "$(printf '%s|' "${array[@]}" | sed 's/|$//')" file

Atau mungkin:

grep -E "$(export IFS="|"; echo "${array[*]}")" file

Secara keseluruhan, itu lebih baik, lebih cepat dan lebih mudah untuk menggunakan file dan melupakan array.

StéphaneChazelas Sep 10 2020 at 17:18
grep "${array[*]}" Textfile

Akan berfungsi selama Anda menyetel IFSke baris baru (atau apa pun yang dimulai dengan baris baru), dan menggunakan --atau -euntuk memastikannya tetap berfungsi meskipun elemen pertama diawali dengan -.

"${array[*]}"dalam kerangka mirip Korn, seperti "$*"pada kerangka POSIX meluas ke daftar elemen yang digabungkan dengan karakter pertama $IFS. Nilai defaultnya $IFSadalah <SPC><TAB><NL>( <SPC><TAB><NL><NUL>dalam zsh), jadi secara default, Anda akan mendapatkan elemen yang digabungkan dengan karakter SPC. Karena grep, Anda memerlukan regex yang berbeda untuk menjadi baris baru yang dipisahkan untuk grepmelakukan perulangan untuk masing-masing secara bergantian.

IFS=$'\n'
grep -e "${array[*]}" file

Dalam zsh, melakukan grep -e "${(pj:\n:)array}" file(secara eksplisit joining dengan baris baru daripada memodifikasi $IFSsecara global), atau grep -e$^array file(yang memperluas larik dalam fish/ rc-style, di mana itu menjadi grep -efirst -esecond filecara lain untuk menyediakan beberapa pola grep) akan lebih bersih.

Opsi lainnya adalah melakukan:

printf '%s\n' "${array[@]}" | grep -f - file

Kali ini meneruskan daftar pola yang dipisahkan baris baru di atas grepstdin daripada melalui argumen.

Pendekatan tersebut akan bekerja apakah pola Anda adalah string tetap (dengan -F), ekspresi reguler yang diperpanjang (dengan -E) atau ekspresi reguler dasar (default).

Anda mungkin ingin memastikan daftar pola tidak kosong:

(( ${#array[@]} > 0 )) && grep ...

Memanggil grepdengan pola kosong memberikan hasil yang berbeda bergantung pada grepimplementasinya dan biasanya bukan yang Anda inginkan.