Cara yang lebih baik untuk melakukan “echo $ x | sed ... "dan" echo $ x | Grep ... "

14

Saya sering menemukan ini dalam skrip (dan, saya harus akui, tulis sendiri):

a=$(echo "$x" | sed "s/foo/bar/")

atau

if echo "$x" | grep -q foo
then
    ...
fi

Pertimbangkan "foo" untuk memasukkan beberapa hal regex.

Saya merasa bahwa harus ada - dan kemungkinan besar adalah - cara yang lebih baik untuk frase ini, yang tidak melibatkan dua perintah dan pipa tetapi membungkusnya menjadi beberapa ekspresi yang lebih kompak.

Saya tidak bisa menemukannya. Siapa saja?

DevSolar
sumber
Saya berharap ungkapan ini sering digunakan karena kombinasi ketidaktahuan (tidak tahu alternatif) dan rawatan (mengetahui alternatif tetapi memilih ini sebagai yang lebih mudah untuk dipahami). Saya bisa melihat sekilas apa contoh Anda lakukan, tetapi saya perlu referensi shell untuk mencari tahu alternatif dalam jawaban grawity dan Dan McG.
quack quixote
3
Ngomong-ngomong, metode yang disukai untuk melakukan penggantian perintah adalah dengan $()daripada backticks.
Dijeda sampai pemberitahuan lebih lanjut.
2
Ini juga merupakan ide yang baik untuk mengutip ekspansi sehingga spasi putih dilindungi: a="$(echo "$x" | sed "s/foo/bar/")"dan if echo "$x" | grep foo; ….
Chris Johnsen
Kata sambutan bagus pada $ () vs. ``. Saya melihat kemampuan bash saya belum terlalu bagus.
DevSolar

Jawaban:

12

Kecuali Anda mengasumsikan shell tertentu, tidak ada cara yang lebih baik untuk melakukan ini selain "pipe echo to tool" (atau hanya "tool" itu sendiri seperti expr ); hanya itu yang bisa Anda andalkan dengan cangkang Bourne tradisional dan / atau cangkang POSIX . Jika Anda mempertimbangkan cangkang lain, maka ada beberapa kemungkinan bawaan lainnya.

ksh punya

  • pola ekstra: ?(pattern-list), *(pattern-list), {n}(pattern-list), {n,m}(pattern-list), @(pattern-list), !(pattern-list);
  • yang %P printf specifier untuk mengkonversi ekspresi reguler diperpanjang ke dalam pola (dan %Runtuk ekspresi reguler diperpanjang pola);
  • yang expr == patternkondisi di [[ expr ]]tes;
  • yang ${param/pattern/replacement}ekspansi parameter.

bash punya

  • yang extglobpilihan untuk mengaktifkan sebagian besar pola tambahan ksh (tidak ada {n}dan {n,m});
  • yang expr == patternkondisi (di [[ expr ]]tes);
  • yang ${param/pattern/replacement}ekspansi parameter;
  • (dalam versi yang lebih baru) expr =~ extregexpkondisi (dalam [[ expr ]]pengujian) yang dapat cocok dengan ekspresi reguler yang diperluas
    • dengan subekspresi tanda kurung dan BASH_REMATCHparameter, penggantian gaya sed dapat dilakukan.

zsh memiliki

  • polanya sendiri diperluas dengan EXTENDED_GLOBopsi;
  • ksh - seperti pola yang diperluas dengan KSH_GLOBopsi;
  • yang expr == patternkondisi (di [[ expr ]]tes);
  • yang ${pattern/pattern/replacement}ekspansi parameter;
  • yang expr =~ extregexpkondisi (di [[ expr ]]tes) yang bisa cocok melawan ekspresi reguler diperpanjang,
    • dapat menggunakan PCRE alih-alih ekspresi reguler biasa yang diperluas jika opsi RE_MATCH_PCRE disetel,
    • dengan subekspresi tanda kurung, MATCHparameter, dan matchparameter (atau BASH_REMATCHdengan BASH_REMATCHset opsi), penggantian gaya sed dapat dilakukan;
  • yang zsh/pcremodul yang menawarkan pcre_compile, pcre_studydan pcre_matchperintah dan -pcre-match tes kondisi (di [[ expr ]]tes);
  • yang zsh/regexmodul yang menawarkan -regex-match tes kondisi (di [[ expr ]]tes).
Chris Johnsen
sumber
Wow. Selengkap yang bisa orang harapkan. Pasti pemenang.
DevSolar
6

Untuk mengganti garis sed, lakukan sesuatu seperti

${a/foo/bar} atau ${a//foo/bar}

Dalam bentuk pertama, hanya instance pertama yang diganti. Bentuk kedua adalah pencarian global & ganti.

Dalam kasus Anda, itu akan menjadi

Dari pada:

if echo $x | grep foo
then
    ...
fi

Pertimbangkan menggunakan:

if [ $x =~ foo ]
then
    ...
fi

Di mana fooekspresi reguler.

Dan McGrath
sumber
Di mana cangkang ini bekerja?
Peltier
1
if [ $x =~ foo ]memberikan pesan kesalahan di sini. if [[ $x =~ foo ]]Namun, bekerja dengan baik. Terima kasih!
DevSolar
1
Semua ini adalah bashism, mereka membutuhkan bash. Selain itu, =~membutuhkan bash> = 3 iirc.
ℝaphink
1
Selain itu, foo bukan ekspresi reguler (seperti pada PCRE) dalam contoh ini, tetapi menggunakan wildcard. Ada lebih banyak kebaikan bash ini di manual bash, bagian "Parameter Expansion". Saya sangat menghargai hal-hal seperti ${a##foo}dan ${a%%bar}.
ℝaphink
3
Operator pertandingan =~memang menggunakan ekspresi reguler. Ini benar, misalnya: [[ "abcdddde" =~ ^a.*d+.g* ]]( misalnya nol atau lebih g daripada wildcard, misalnya).
Dijeda sampai pemberitahuan lebih lanjut.
2

Cara yang kompatibel dengan posix yang baik untuk menguji apakah suatu variabel berisi pola adalah:

test ${var##*foo*} || <do something>;

Sintaks dari perluasan parameter adalah:

 ${parameter##pattern}

di mana patternadalah pola shell .

mrucci
sumber