Menggunakan perintah grep dengan array kata
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
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.
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.