Saya punya skrip kecil ini di sh
(Mac OSX 10.6) untuk melihat melalui array file. Google telah berhenti membantu pada saat ini:
files="*.jpg"
for f in $files
do
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
echo $name
done
Sejauh ini (jelas, bagi Anda shell guru) $name
hanya memegang 0, 1 atau 2, tergantung pada apakah grep
ditemukan bahwa nama file cocok dengan masalah yang diberikan. Yang saya suka adalah untuk menangkap apa yang ada di dalam parens ([a-z]+)
dan menyimpannya ke variabel .
Saya hanya ingin menggunakan grep
, jika memungkinkan . Jika tidak, tolong jangan Python atau Perl, dll. sed
Atau semacamnya - Saya baru mengenal shell dan ingin menyerang ini dari sudut * nix purist.
Juga, sebagai bonu super keren , saya ingin tahu bagaimana saya bisa menggabungkan string dalam shell? Apakah grup yang saya tangkap adalah string "somename" yang disimpan dalam $ name, dan saya ingin menambahkan string ".jpg" ke akhir, benarkan cat $name '.jpg'
?
Tolong jelaskan apa yang terjadi, jika Anda punya waktu.
grep
, makased
akan lebih bagus, jika mungkin untuk menyelesaikan menggunakansed
.Jawaban:
Jika Anda menggunakan Bash, Anda bahkan tidak perlu menggunakan
grep
:Lebih baik menempatkan regex dalam sebuah variabel. Beberapa pola tidak akan berfungsi jika dimasukkan secara harfiah.
Ini menggunakan
=~
yang merupakan operator pertandingan regex Bash. Hasil pertandingan disimpan ke array yang disebut$BASH_REMATCH
. Grup tangkapan pertama disimpan dalam indeks 1, yang kedua (jika ada) dalam indeks 2, dll. Indeks nol adalah kecocokan penuh.Anda harus menyadari bahwa tanpa jangkar, regex ini (dan yang menggunakan
grep
) akan cocok dengan salah satu contoh berikut dan lebih banyak lagi, yang mungkin bukan yang Anda cari:Untuk menghilangkan contoh kedua dan keempat, buat regex Anda seperti ini:
yang mengatakan string harus dimulai dengan satu digit atau lebih. Karat mewakili awal dari string. Jika Anda menambahkan tanda dolar di akhir regex, seperti ini:
maka contoh ketiga juga akan dihilangkan karena titik tidak ada di antara karakter di regex dan tanda dolar mewakili akhir dari string. Perhatikan bahwa contoh keempat juga gagal dalam pertandingan ini.
Jika Anda memiliki GNU
grep
(sekitar 2,5 atau lebih baru, saya pikir, ketika\K
operator ditambahkan):The
\K
operator (variable-length lihat-balik) menyebabkan pola sebelumnya untuk pertandingan, tetapi tidak termasuk pertandingan dalam hasil. Persamaan panjang tetap adalah(?<=)
- pola akan dimasukkan sebelum tanda kurung tutup. Anda harus menggunakan\K
jika bilangan dapat mencocokkan string panjang yang berbeda (misalnya+
,*
,{2,4}
).The
(?=)
pertandingan operator fixed atau pola variabel-panjang dan disebut "tampak-depan". Itu juga tidak termasuk string yang cocok dalam hasil.Untuk membuat korek api tidak peka,
(?i)
operator digunakan. Ini mempengaruhi pola yang mengikutinya sehingga posisinya signifikan.Regex mungkin perlu disesuaikan tergantung pada apakah ada karakter lain dalam nama file. Anda akan perhatikan bahwa dalam kasus ini, saya menunjukkan contoh menggabungkan string pada saat yang sama ketika substring ditangkap.
sumber
/K
batu operator.grep
. Itu juga diterima oleh OP dan cukup banyak dipilih. Terima kasih untuk downvote.Ini tidak mungkin dengan murni
grep
, setidaknya tidak secara umum.Tetapi jika pola Anda cocok, Anda mungkin dapat menggunakan
grep
beberapa kali dalam pipa untuk mengurangi garis Anda menjadi format yang dikenal, dan kemudian mengekstrak hanya sedikit yang Anda inginkan. (Meskipun alat sukacut
dansed
jauh lebih baik dalam hal ini).Misalkan demi argumen bahwa pola Anda sedikit lebih sederhana:
[0-9]+_([a-z]+)_
Anda dapat mengekstraksi seperti ini:Yang pertama
grep
akan menghapus baris apa pun yang tidak cocok dengan pola keseluruhan Anda, yang keduagrep
(yang telah--only-matching
ditentukan) akan menampilkan bagian alfa nama. Ini hanya berfungsi karena polanya cocok: "porsi alfa" cukup spesifik untuk mengeluarkan apa yang Anda inginkan.(Selain: Secara pribadi saya akan menggunakan
grep
+cut
untuk mencapai apa yang Anda inginkan:.echo $name | grep {pattern} | cut -d _ -f 2
Ini akancut
menguraikan baris ke dalam bidang dengan memisahkan pada pembatas_
, dan mengembalikan hanya bidang 2 (angka bidang mulai dari 1)).Filosofi Unix adalah memiliki alat yang melakukan satu hal, dan melakukannya dengan baik, dan menggabungkannya untuk mencapai tugas-tugas non-sepele, jadi saya berpendapat bahwa
grep
+sed
etc adalah cara yang lebih Unixy dalam melakukan sesuatu :-)sumber
for f in $files; do name=
echo $ f | grep -oEi '[0-9] + _ ([az] +) _ [0-9a-z] *' | cut -d _ -f 2;
Aha!Saya menyadari bahwa jawaban sudah diterima untuk ini, tetapi dari "sudut pandang murni * nix" sepertinya alat yang tepat untuk pekerjaan itu
pcregrep
, yang sepertinya belum disebutkan. Coba ubah garis:sebagai berikut:
untuk mendapatkan hanya konten dari grup penangkap 1.
The
pcregrep
Alat memanfaatkan semua sintaks yang sama yang telah digunakan dengangrep
, tapi alat fungsi yang Anda butuhkan.Parameter
-o
berfungsi sepertigrep
versi jika telanjang, tetapi juga menerima parameter numerikpcregrep
, yang menunjukkan grup tangkapan mana yang ingin Anda tampilkan.Dengan solusi ini ada minimal perubahan yang diperlukan dalam skrip. Anda cukup mengganti satu utilitas modular dengan yang lain dan mengubah parameternya.
Catatan Menarik: Anda dapat menggunakan argumen -o multipel untuk mengembalikan beberapa grup tangkapan sesuai urutan yang ditampilkan di telepon.
sumber
pcregrep
tidak tersedia secara defaultMac OS X
yang digunakan OPpcregrep
Tampaknya saya tidak mengerti digit setelah-o
: "Huruf opsi tidak dikenal '1' dalam" -o1 ". Juga tidak disebutkan fungsi itu ketika melihatpcregrep --help
7.8 2008-09-05
.echo 'r123456 foo 2016-03-17' | pcregrep -o1 'r([0-9]+)' 123456
pcregrep
8.41 (diinstal denganapt-get install pcregrep
aktifUbuntu 16.03
) tidak mengenali-Ei
sakelar. Ini bekerja sempurna tanpanya. Pada macOS, denganpcregrep
diinstal viahomebrew
(juga 8,41) seperti @anishpatel menyebutkan di atas, setidaknya pada High Sierra-E
saklar juga tidak dikenali.Tidak mungkin hanya dalam grep saya percaya
untuk sed:
Saya akan coba bonus ini:
sumber
sed
solusi itu tidak berhasil. Itu hanya mencetak semua yang ada di direktori saya.Ini adalah solusi yang menggunakan gawk. Ini adalah sesuatu yang saya pikir perlu saya gunakan berulang kali jadi saya membuat fungsi untuk itu
untuk menggunakan lakukan
sumber
\s
. Apakah Anda tahu cara memperbaikinya?Saran untuk Anda - Anda dapat menggunakan ekspansi parameter untuk menghapus bagian nama dari garis bawah terakhir dan seterusnya, dan demikian pula di awal:
Maka
name
akan memiliki nilaiabc
.Lihat dokumen pengembang Apple , cari 'Ekspansi Parameter'.
sumber
jika Anda memiliki bash, Anda dapat menggunakan globbing yang diperluas
atau
sumber