Чому `sed expr1 | sed expr2` відрізняється від `sed -e expr1 -e expr2`


10

Я розбивав вихід, idщоб забезпечити більш читабельний список за списком груп, учасником якого є користувач:

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

Я хотів відокремити номер групи від її накресленої назви, тому я продовжив вираз, як це

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

Однак це діяло не так, як я спочатку очікував. Другий вираз, схоже, не мав ефекту.

Натомість, щоб отримати бажаний результат, мені потрібно було виконати дві окремі sedкоманди, наприклад:

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)

Чому мені потрібні дві sedкоманди в трубі, а не одна з кількома інструкціями? Або якщо я можу це зробити з одним sed, як би це зробити?

Що я особливо хотів би, щоб це було єдиний пробіл між значенням UID / GID та його накресленою назвою для кожного окремого елемента (включаючи UID та GID в першому рядку), але застереження полягає в тому, що в моїх реальних даних я можу мати групи містять дужки у своїх іменах, і я не хочу, щоб самі імена були підроблені.

Відповіді:


14

sed, Як awkі cutчи perl -neроботи по кожній лінії в окремо один за іншим.

sed -e code1 -e code2

насправді працює як:

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

Якщо ваш код2 2,$ s/foo/bar/, це:

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

Оскільки ваш вхід має лише один рядок, запит sub()ніколи не буде виконуватися.

Вставлення символів нового рядка в простір шаблону code1не linenumberзбільшує.

Натомість у вас є один простір шаблону з кількома рядками в ньому під час обробки першого та єдиного рядка введення. Якщо ви хочете змінити другий рядок і більше цього багаторядкового простору шаблону, вам потрібно зробити щось на кшталт:

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

Хоча тут, звичайно, ви також можете зробити дві операції за один раз:

id | sed 's/,\([^(]*\)(/\n\t\1 (/g'

awk, і perl -n / p, працює на кожній записи, яка за замовчуванням до рядка, але може бути змінена; в цьому випадку -vRS=,або -054може допомогти.
dave_thompson_085

5

Якщо у вас є GNU sed, ви можете використовувати

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

який додає пробіл перед 4-ю та наступними відкритими дужками, а потім замінює коми.


1
Це виглядає цікаво. На жаль, це також впливає на назви груп, які містять дужки, такі як мій приклад international (uk) location, шляхом вставки небажаного пробілу в самому імені.
roaima

Тоді використовуйте, s/\([[:digit:]]\+\)(/\1 (/4gщо додасть пробіл лише у тому випадку, якщо перед дужками є цифри.
glenn jackman

1

Те, що сказав @ stéphane-chazelas, відповідає дійсності, але ви завжди можете додати простір і розділити на рядки після цього:

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

Або в одному сценарії sed (без -e):

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

Ми зазвичай використовуємо " /" розділювач пошуку команд, але він також приймає будь-які символи, тому іноді простіше читати, використовуючи інші символи типу " :", щоб уникнути комбінацій, таких як " /\".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.