Grep untuk pola di awal atau di tengah garis

9

Saya akan mulai dengan mengatakan bahwa saya pikir masalah ini sedikit tidak bersalah daripada kedengarannya.

Yang perlu saya lakukan: periksa folder dalam variabel lingkungan PATH. Itu bisa di awal atau di suatu tempat setelah. Saya hanya perlu memverifikasi bahwa folder itu ada di sana.

Contoh masalah saya - mari kita gunakan /opt/gnome.


SKENARIO 1: folder bukan di awal PATH

# echo "$PATH"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

# echo "$PATH" | grep ":/opt/gnome"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Perhatikan bahwa grep harus cukup spesifik sehingga tidak menangkap /var/opt/gnome. Karena itu usus besar.


SKENARIO 2: folder berada di awal PATH.

# echo "$PATH"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

# echo "$PATH" | grep "^/opt/gnome"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Ini masalah saya - saya perlu mencari titik dua atau titik awal dengan folder ini. Apa yang ingin saya lakukan adalah salah satu dari dua ungkapan braket ini:

# echo $PATH | grep "[^:]/opt/gnome"
# echo $PATH | grep "[:^]/opt/gnome"

TAPI [^dan [:punya artinya sendiri. Karena itu, kedua perintah di atas tidak berfungsi.

Apakah ada cara saya bisa memahami dua skenario ini dalam satu perintah?

JamesL
sumber
Perhatikan bahwa komentar Gilles tentang jawaban Costas juga berlaku untuk pertanyaan: karena Anda tidak mengerti /opt/gnome:atau /opt/gnome$, Anda akan menemukan /opt/gnome-fooatau /opt/gnome/bar.
Scott
@Scott - Selama Anda memasukkan dalam ruang pertandingan Anda pertandingan, Anda selalu dapat menempelkan tali apa pun ke kepala dan ekor garis tanpa komplikasi seperti itu. Sama sepertigrep '^\(any number of other matches:*:\)*my match\(:.*\)*$'
mikeserv

Jawaban:

10

Jika Anda memeriksa konten PATHvariabel lingkungan, bukan mencari sesuatu dalam file, maka itu grepadalah alat yang salah. Lebih mudah (dan lebih cepat dan bisa dibilang lebih mudah dibaca) untuk melakukannya di shell.

Dalam bash, ksh dan zsh:

if [[ :$PATH: = *:/opt/gnome:* ]]; then
 : # already there
else
  PATH=$PATH:/opt/gnome
fi

Mudah dibawa:

case :$PATH: in
  *:/opt/gnome:*) :;; # already there
  *) PATH=$PATH:/opt/gnome;;
esac

Perhatikan penggunaan :$PATH:daripada $PATH; dengan cara ini, komponen selalu dikelilingi oleh titik dua dalam string pencarian bahkan jika itu pada awal atau akhir $PATH.

Jika Anda mencari melalui satu baris file, maka Anda dapat menggunakan regexp tambahan (yaitu membutuhkan grep -E) (^|:)/opt/gnome($|:)untuk mencocokkan /opt/gnometetapi hanya jika itu pada awal baris atau mengikuti titik dua, dan hanya jika itu pada akhir garis atau diikuti oleh titik dua.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
8

Anda dapat menggunakan ekspresi reguler yang diperluas dengan hanya menggunakan grep -E

Anda harus mencocokkan awal dan akhir jalan yang Anda coba temukan jika Anda ingin menghindari kesalahan positif.

Cocok dengan instance di awal:

$ TEST=/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Juga cocok dengan instance di tengah:

$ TEST=/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Menghindari kesalahan positif:

$ TEST="/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta"
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"

Tidak ada yang cocok di sana.

Kompak dan elegan. Diuji pada Debian 7.

Luis Antolín Cano
sumber
1
egrepadalah penggunaan yang dihentikan grep -E(sumber: man grep)
Anthon
Terima kasih, bekerja seperti pesona! Saya tidak memilihnya sebagai jawabannya, karena saya pikir opsi -w sedikit lebih sederhana. Bahkan lebih sederhana dari yang saya bayangkan sebelumnya!
JamesL
3
Peringatan. The -wpilihan memiliki beberapa masalah. Hanya digit, huruf, dan garis bawah yang dianggap "kata". Jadi beberapa karakter yang tidak biasa tetapi mungkin akan membuatnya gagal. Contoh echo '/sbin:/usr/sbin:/var-/opt/gnome' | grep -w "/opt/gnome"dan echo '/sbin:/usr/sbin:/var./opt/gnome' | grep -w "/opt/gnome". Itu memberikan hasil yang salah.
Luis Antolín Cano
1
Anda berada di jalur yang benar, tetapi masih ada positif palsu: /opt/gnome/somethingelse.
Gilles 'SO- stop being evil'
1
Benar benar. Kita harus peduli pada akhir secara eksplisit, bukan hanya permulaan. Saya pikir ini memperbaiki masalah echo "/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta" | grep -E "(:|^)/opt/gnome(:|$)". Mengedit jawaban.
Luis Antolín Cano
7

Jika Anda tidak menikah grep, Anda dapat menggunakan awkdan memisahkan catatan:

awk 'BEGIN {RS=":"} /^\/opt\/gnome$/'
jasonwryan
sumber
5

Anda juga bisa menggunakannya

echo "$PATH" | tr ':' '\n' | grep -x "/opt/gnome"

yang membagi variabel path menjadi baris terpisah (satu per path), sehingga grep -xdapat mencari hasil yang tepat. Ini tentu saja memiliki kekurangan yang membutuhkan proses tambahan tr. Dan itu tidak akan berfungsi ketika nama folder di PATHmengandung karakter baris baru.

TBrandt
sumber
2

Saya tidak tahu apakah cukup untuk menjawab tetapi

grep -w "/opt/gnome"

akan memuaskan kebutuhan anda.

echo '/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome
echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome

tapi

echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep "/opt/gnome" -o
/opt/gnome
/opt/gnome
Costas
sumber
Ini berfungsi dengan baik karena titik dua adalah karakter non-kata. Terima kasih!
JamesL
@ Sman865 Ada alasan lain: karena /bukan bagian dari kata tetapi radalah.
Costas
2
Peringatan. Seperti yang saya katakan di komentar pada jawaban saya. Ada karakter hukum untuk nama direktori yang bukan karakter kata. Itu mengarah pada hasil yang salah. Tidak biasa untuk mengakhiri nama direktori - tetapi itu bisa terjadi.
Luis Antolín Cano
4
@ Sman865 Positif palsu: /opt/gnome-beta,, /home/bob/opt/gnome...
Gilles 'SO- berhenti menjadi jahat'
grep -w /usr/local -o <<< /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games/usr/local /usr/local /usr/local
Kasing
0

Untuk pilih /opt/gnomedikelilingi oleh karakter non-kata (baris baru, :, /, dll) mencoba yang satu ini:

grep '\B/opt/gnome'
jimmij
sumber
0

Anda dapat melakukan ini dengan andal dan dengan sedikit usaha di grep. Anda dapat memanfaatkan ekstensi yang tersedia secara luas dan di antaranya banyak solusi telah ditawarkan, tetapi bahkan dengan regex dasar hal itu mudah dilakukan, meskipun mungkin tidak secara intuitif jadi pada pandangan pertama.

Dengan regex dasar - dan sebagainya grep- Anda selalu memiliki dua jangkar yang dapat diandalkan - kepala dan ekor garis. Anda dapat mengaitkan kecocokan dengan keduanya terlepas dari lokasinya di baris seperti:

grep '^\(ignore case, delimiter\)*match\(delimiter, ignore case\)*$'

grepakan cocok dari kepala baris sebanyak kemunculan \(grouped\)subekspresi yang harus dihadapi di samping pembatas Anda kemudian pertandingan eksplisit Anda, dan dari ekor pertandingan Anda ke ekor garis dengan cara yang sama. Jika kecocokan eksplisit Anda tidak dicocokkan secara eksplisit itu akan gagal dan tidak mencetak apa pun.

Dan itulah yang mungkin Anda lakukan, misalnya:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$'

Lihat diri mu sendiri:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$
' <<\INPUT
/opt/gnome-beta
/opt/gnome
/home/bob/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt-gnome-beta
/opt/gnomenot::::/opt/gnome
INPUT

KELUARAN

/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt/gnomenot::::/opt/gnome
mikeserv
sumber
0

Anda telah memperhatikan kasing tepi ... Anda bisa menghindarinya dengan memaksa penampakan a: di awal baris:

 echo ":$PATH" | grep ":/opt/gnome"

atau jika jalurnya tepat tambahkan juga satu di akhir untuk memastikan itu dibatasi:

 echo ":${PATH}:" | grep ":/opt/gnome:"
Olivier Dulac
sumber