Mengapa `sed expr1 | sed expr2` berbeda dengan `sed -e expr1 -e expr2`

10

Saya memisahkan output dari iduntuk menyediakan daftar grup yang lebih mudah dibaca dari grup di mana pengguna menjadi anggota:

id roaima | sed 's/,/\n\t/g'
uid=1001(roaima) gid=1001(roaima) groups=1001(roaima)
    24(cdrom)
    25(floppy)
    ...
    822413650 (international (uk) location)

Saya ingin memisahkan nomor grup dari nama kurung sehingga saya memperluas ekspresi seperti ini

id roaima | sed -e 's/,/\n\t/g' -e '2,$s/(/ (/'

Namun, ini tidak bertindak seperti yang saya harapkan pada awalnya. Ekspresi kedua tampaknya tidak berpengaruh.

Sebagai gantinya, untuk mendapatkan hasil yang saya inginkan, saya perlu menjalankan dua sedperintah terpisah , seperti ini:

id roaima | sed -e 's/,/\n\t/g' | sed '2,$s/(/ (/'
uid=1001(roaima) gid=1001(roaima) groups=1001(roaima)
    24 (cdrom)
    25 (floppy)
    ...
    822413650 (international (uk) location)

Mengapa saya perlu dua sedperintah dalam pipa daripada satu dengan banyak instruksi? Atau jika saya bisa melakukan ini dengan satu sed, bagaimana saya melakukannya?

Apa yang saya terutama inginkan adalah memiliki ruang tunggal antara nilai UID / GID dan nama kurung untuk setiap item (termasuk UID dan GID di baris pertama), tetapi peringatannya adalah bahwa dalam data nyata saya, saya dapat memiliki grup mengandung tanda kurung dalam nama mereka dan aku tidak ingin nama itu hancur.

roaima
sumber

Jawaban:

14

sed, suka awkatau cutatau perl -nebekerja pada setiap baris satu per satu.

sed -e code1 -e code2

sebenarnya dijalankan sebagai:

while(patternspace = getline()) {
  linenumber++
  code1
  code2
} continue {print patternspace}

Jika kode2 Anda adalah 2,$ s/foo/bar/, itu:

if (linenumber >= 2) sub(/foo/, "bar", patternspace)

Karena input Anda hanya memiliki satu baris, maka sub()tidak akan pernah berjalan.

Memasukkan karakter baris baru di ruang pola di code1tidak membuat linenumberpeningkatan.

Sebagai gantinya, Anda memiliki satu ruang pola dengan beberapa garis di dalamnya saat memproses input pertama dan satu-satunya. Jika Anda ingin melakukan modifikasi pada baris kedua dan lebih dari ruang pola multi-baris itu, Anda perlu melakukan sesuatu seperti:

s/\(\n[^(]*\)(/\1 (/g

Meskipun di sini tentu saja, Anda sebaiknya melakukan dua operasi sekaligus:

id | sed 's/,\([^(]*\)(/\n\t\1 (/g'
Stéphane Chazelas
sumber
awk, dan perl -n / p, bekerja pada setiap record yang default ke sebuah baris tetapi dapat diubah; dalam hal ini -vRS=,atau -054bisa membantu.
dave_thompson_085
5

Jika Anda memiliki GNU sed, Anda bisa menggunakannya

id username | sed 's/(/ (/4g; s/,/\n\t/g'

yang menambahkan spasi sebelum tanda kurung terbuka ke-4 dan berikutnya, kemudian menggantikan koma.

glenn jackman
sumber
1
Itu terlihat menarik. Sayangnya itu juga memengaruhi international (uk) locationnama-nama grup yang berisi tanda kurung seperti contoh saya ,, dengan menyisipkan ruang yang tidak diinginkan dalam nama itu sendiri.
roaima
Kemudian gunakan s/\([[:digit:]]\+\)(/\1 (/4gyang hanya akan menambah spasi jika ada angka sebelum tanda kurung.
glenn jackman
1

Apa yang dikatakan @ stéphane-chazelas benar, tetapi Anda selalu dapat menambahkan spasi terlebih dahulu dan membaginya menjadi seperti ini:

sed -e 's:\([,=][0-9]*\):\1 :g' -e 's:,:\n\t:g'

Atau dalam satu skrip sed (tanpa -e):

sed 's:\([,=][0-9]*\):\1 :g; s:,:\n\t:g'

Kami biasanya menggunakan " /" sebagai pemisah dari pencarian perintah, tetapi ia menerima karakter apa pun juga, jadi kadang - kadang lebih mudah dibaca menggunakan karakter lain seperti " :" untuk menghindari kombinasi seperti " /\".

WPomier
sumber