Menggunakan sed dapatkan substring antara dua tanda kutip ganda

14

Saya punya file

xyz... rsync: "/home/path/to/file": Permission denied (13) rsync:
"/home/path/to/file1": Permission denied (13) rsync:
"/home/path/to/file2": Permission denied (13) rsync:
"/home/path/to/file3": Permission denied (13)

Sekarang saya ingin mengekstrak path file saja dan menyimpannya ke file lain. File output seperti:

/home/path/to/file 
/home/path/to/file1 
/home/path/to/file2
/home/path/to/file3

Menggunakan sed atau awk bagaimana saya bisa melakukan ini?

Saya sudah mencoba sed -n '/"/,/"/p' myfiletetapi tidak berfungsi.

XemX
sumber
3
Bagi mereka yang memilih untuk menutup - Bagaimana ini bisa di luar topik? Ini tentang pemrograman shell !! Itu PEMROGRAMAN yang ON TOPIK untuk Stack Overflow!
Jonathan Leffler
2
Selamat datang di Stack Overflow. Seperti yang Anda lihat, kadang-kadang kita memiliki masalah dengan orang-orang yang memiliki jari pemicu gatal menutup pertanyaan yang sangat baik (seperti yang ini) dengan alasan buruk untuk penutupan. Itu tidak terjadi terlalu sering (atau, saya tidak bisa melihat masalah tepat waktu terlalu sering), tetapi itu memang terjadi. Jangan lupa membaca FAQ terlalu lama.
Jonathan Leffler

Jawaban:

17

Anda dapat mem-pipe stderr dari perintah rsync Anda ke skrip awk:

awk -F '"' '{print $2}' 

Atau ke perintah cut seperti ini:

cut -d'"' -f2
anubhava
sumber
2
Atau, lebih pendek:cut -d\" -f2
@AndersJohansson: Terima kasih saya menambahkan perintah cut Anda untuk menjawab juga.
anubhava
Saya pikir ini tidak akan berhasil .. karena Anda dapat melihat jumlah bidang path file tidak tetap $ 2 atau f2 .. Terima kasih!
Sebenarnya rsync akan selalu menulis filepath dulu antara "dan "pada stderr.
anubhava
1
@ Jam88: Sebenarnya, ini akan berhasil karena cara anubbhava menulisnya. Pembatas bidang diatur ke tanda kutip ganda. Itu berarti bahwa semuanya sampai dengan kutipan ganda pertama (mungkin string kosong) adalah $1; segala sesuatu antara tanda kutip ganda pertama dan kedua adalah $2; dan semuanya setelah kutipan ganda kedua ada di $3( $4, ...). Nama file (tampaknya) selalu berada di antara dua tanda kutip ganda pertama, jadi solusi ini harus berfungsi (dan lakukan ketika saya mengujinya).
Jonathan Leffler
6

Menggunakan sed:

sed 's/^[^"]*"\([^"]*\)".*/\1/'

Yang terlihat untuk: awal baris, serangkaian non-kutipan, kutipan ganda, menangkap serangkaian tanda kutip, kutipan ganda dan apa pun di baris, dan menggantinya dengan materi yang ditangkap.

$ sed 's/^[^"]*"\([^"]*\)".*/\1/' <<'EOF'
> xyz... rsync: "/home/path/to/file": Permission denied (13) rsync:
> "/home/path/to/file1": Permission denied (13) rsync:
> "/home/path/to/file2": Permission denied (13) rsync:
> "/home/path/to/file3": Permission denied (13)
> EOF
/home/path/to/file
/home/path/to/file1
/home/path/to/file2
/home/path/to/file3
$

Uji pada RHEL 5 Linux dengan GNU sed, tetapi hanya menggunakan fitur yang akan bekerja dalam versi 7 UNIX ™ sed.

Kebetulan, cara yang sedikit lebih sederhana untuk melakukannya adalah dengan dua perintah pengganti; ubah semuanya hingga dan termasuk kutipan ganda pertama menjadi string kosong (itu adalah urutan nol atau lebih non kutipan diikuti oleh kutipan ganda); ubah semuanya setelah apa yang sekarang menjadi kutipan ganda pertama menjadi nol:

sed 's/^[^"]*"//; s/".*//'

Secara kebetulan, perintah yang Anda coba (`sed -n '/" /, / "/ p') mencetak dari satu baris yang berisi penawaran ganda ke baris berikutnya yang berisi penawaran ganda, tanpa mengedit baris sama sekali. Itulah mengapa itu tampaknya tidak berhasil untuk Anda - itu melakukan apa yang Anda minta, tetapi apa yang Anda minta untuk lakukan bukanlah apa yang ingin Anda lakukan.

Dari sisi efisiensi, tidak mungkin ada perbedaan yang terukur dalam kinerja. Dalam hal kemudahan perawatan, saya kira yang terakhir kurang membebani sel-sel otak.

Jonathan Leffler
sumber
1

Jika versi Anda grepmendukung Perl-regexp:

grep -oP '(?<=")/home/.*?(?=")' file >> anotherfile

Hasil:

/home/path/to/file
/home/path/to/file1
/home/path/to/file2
/home/path/to/file3

Anda juga bisa membuat ini tidak terlalu ketat, untuk mencocokkan apa pun di antara ganda jika Anda menginginkan:

grep -oP '(?<=")[^"]*' file >> anotherfile
Steve
sumber
Apakah Anda perlu membuat yang .*tidak rakus dengan .*?berjaga-jaga kalau-kalau ada kutipan ganda tambahan di baris berikutnya? Atau gunakan [^"]*di tempat .*?
Jonathan Leffler
-1

Gunakan operator >> untuk menyimpan output apa pun ke file.

Suka

grep -r "pattern" * >> file.txt

Jadi ubah saja untuk skenario spesifik Anda menggunakan sed dengan menambahkan

>> filename

ke perintah

AStupidNoob
sumber
Itu grep -rmelakukan pencarian rekursif melalui direktori yang tercantum dalam argumen ( *). Tidak jelas pola apa yang ada dalam pikiran Anda, tetapi grepakan mengambil seluruh garis. Tujuan latihan ini adalah untuk mengumpulkan informasi dari bagian garis. Jika Anda menggunakan GNU grep, ada beberapa cara untuk melakukannya ( -o); ini adalah non-standar (kecuali sejauh GNU mendefinisikan standar de facto). Demikian pula dengan penggunaan ekspresi reguler PCRE; itu adalah ekstensi GNU lain. Mereka baik-baik saja jika Anda memiliki GNU grepdan tidak ada rencana untuk bekerja pada platform di mana GNU greptidak tersedia secara default.
Jonathan Leffler
Maaf saya melewatkan itu, saya pikir dia ingin tahu secara umum apa yang harus dilakukan untuk menempatkan output ke dalam file, dan grep hanyalah sebuah contoh.