Як команда sed '1! G; h; $! D' змінює вміст файлу?


20

Моє запитання пов'язане із sedспецифічним рішенням, поданим у цій відповіді на це запитання зворотного зіставлення . sed/ grepРішення , яке я не можу розшифрувати це наступним:

sed '1!G;h;$!d' file

Може хтось, будь ласка, розшифрувати цю команду?

Я знаю з VI (M) знання, що G позначає останній рядок файлу і що в sed bang (!) Після цього адреса працює трохи так grep -v, щоб сказати, що він не буде відповідати цьому рядку. Але в цілому вищевказаний сценарій inline sed - це поза мною.


3
... дуже повільно ...
mikeserv

1
Як натякає mikeserv. Слід зазначити, що цей хитрий sedрецепт є надзвичайно неефективним способом (O (n ^ 2/2) складності) повернути рядки у файлі. Це було б надмірно повільно для файлів з великою кількістю рядків. Більш ефективну альтернативу відміни порядку замовлення див. У tacGNU coreutils.
аріельф

Відповіді:


35

Це повертає файл за рядком.

sed '1! g; h; $! d' файл

По-перше, sedє простір утримування та простір візерунка . Ми повинні розрізняти їх, перш ніж концентруватися на цій конкретній команді.

Коли sedчитається новий рядок, він завантажується в простір шаблону. Тому цей простір перезаписується щоразу, коли обробляється новий рядок. З іншого боку, простір утримування є послідовним протягом усієї обробки, і значення можуть зберігатися там для подальшого використання.


До команди:

Є 3 команди в цій заяві: 1!G, hі$!d

  • 1!Gозначає, що Gкоманда виконується в кожному рядку, крім першого ( !заперечує значення 1). Gозначає додавати те, що знаходиться у просторі утримування, у простір шаблону.

  • hстосується кожного рядка. Він копіює простір шаблону в простір утримування (і перезаписує його).

  • $!dстосується кожного рядка, крім останнього ( $представляє останній рядок, !заперечує його). dце команда для видалення рядка (пробіл візерунка).


  1. Тепер, коли читається перший рядок, sedвиконується hкоманда. Перший рядок копіюється в простір утримування. Потім вона видаляється, оскільки відповідає $!умові. sedпродовжує другий рядок.
  2. Другий рядок відповідає умові 1!(це не перший рядок), і тому простір утримування (який має перший рядок) додається до простору шаблону (який має другий рядок). Після цього в просторі малюнка є другий рядок, за яким слідує перший рядок, розмежований новим рядком. Тепер hкоманда застосовується (як і в кожному рядку); все, що знаходиться в просторі шаблону, копіюється в простір утримування. Третє твердження ( $!d) застосовується: рядок видаляється з простору шаблонів.
  3. Крок 2 тепер виконується з усіма рядками. Переходимо до останнього рядка.
  4. В останньому рядку ( $) майже весь етап 2 виконано, але не частина видалення ( d). sed, при виклику без -n, друкує простір шаблону автоматично в кінці обробки для кожного рядка введення. Отже, коли не видалено, друкується пробіл візерунка. Тепер він містить усі рядки у зворотному порядку .

1
@Geek Ні, hкоманда копіює простір шаблону в простір утримування, який зберігається до sedкінця. Після закінчення сценарію все очищається, тому що бінарний вийшов.
хаос

2
Чи можемо ми вважати простір утримування як регістри для vim? Чи вони також пронумеровані? Або є лише одна з них?
Geek

2
@Geek У sedньому є лише один простір для утримування. Це як змінна, яка може щось містити.
хаос

1
@ user1717828 Якби ми не обробляли б перший рядок при обробці. Оскільки sed не викликається, -nми повинні видалити кожен рядок, крім останнього. В останньому рядку sed додає все, від простору утримування до простору шаблону. І оскільки dкоманда не буде виконана, рядок друкується (цей рядок містить тепер увесь файл перевернутий).
хаос

1
(1) Підзапитання / спостереження ОП (у коментарі): «тому hкоманда в останньому рядку є начебто необоротна.» (З додатковим наголосом та злегка перефразованим). Він правий; коли sedобробляється останній рядок вводу , Gзчитується простір утримування (щоб додати його до простору шаблону), а потім hкопіює простір шаблону в простір утримування, на який ніколи більше не посилається . Ми могли так само добре сказати sed 'G;$!h;$!d'або sed 'G;$!{h;d}'. (2) Ми могли б уникнути використання d, сказавши sed -n 'G;h;$p'.
Скотт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.