Збережіть зміни на місці за допомогою awk


135

Я навчаюсь, awkі я хотів би знати, чи є можливість написати зміни у файл, подібно до того, sedде я б застосував -iпараметр для збереження модифікацій у файлі.

Я розумію, що я можу використовувати перенаправлення для написання змін. Однак чи є варіант awkзробити це?


Також див. Serverfault.com/a/547331/313521 для більш загальної відповіді на "редагування файлу на місці з перенаправленням".
Wildcard

@Wildcard. Розчин там жахливо крихкий. Не існує жодних гарантій на впорядкування подій, і використання цього рішення може усікати ваші дані. Як осторонь, я не можу коментувати цей сайт безпосередньо, тому що для цього мені потрібно 50 представників. Я ніколи не зрозумію, чому SO фрагментується на Unix / Linux та адміністратора сервера та ін. ІМО, це була помилка.
Вільям Перселл

@WilliamPursell, "немає гарантії впорядкування подій" - це насправді помилково. Єдина крихкість, яку має рішення, - це якщо довжина вмісту більше, ніж максимальна довжина для команди. Впорядкованість заходів, однак, гарантована.
Wildcard

@Wildcard Який стандарт гарантує замовлення?
Вільям Перселл

@WilliamPursell це гарантується базовою документацією. Для інших снарядів я не знаю. (До речі, якщо ви зв’яжете свій обліковий запис, у вас буде 100 бонусних асоціацій, які ви зможете коментувати.)
Wildcard

Відповіді:


142

В останньому GNU Awk (з 4.1.0 випущено ), він має можливість "замінити" редагування файлів :

[...] Розширення "inplace", побудоване за допомогою нового об'єкта, може використовуватися для імітації функції GNU " sed -i". [...]

Приклад використання:

$ gawk -i inplace '{ gsub(/foo/, "bar") }; { print }' file1 file2 file3

Щоб зберегти резервну копію:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{ gsub(/foo/, "bar") }
> { print }' file1 file2 file3

1
@sudo_O - Дякую за демонстрацію "на місці". Підвищив свою відповідь!
lind

Схоже, варіант, можливо, було видалено? З 4.1.3 у мене є "-i includefile --include = includefile"
Кіт Х'югітт

1
@Keith У мене було те саме питання. Я просто спробував це, і він працює на моєму 4.1.3. inplaceнасправді є бібліотекою, що gawkвідповідає відповіді iiSeymour , тому inplaceце може бути включено як includefile.
cxw

Тут важливий застереження: "бачений" масив буде заповнюватися повторюваними рядками ВСІХ файлів, включених до команди. Отже, якщо кожен файл містить, наприклад, загальний заголовок, він буде видалений у кожному файлі після першого. Якщо замість цього ви хочете обробляти кожен файл самостійно, вам потрібно зробити щось на кшталт f в * .txt; do gawk -i inplace '! saw [$ 0] ++' "$ f"; зроблено
Нік К9

136

Якщо у вас GNU awk 4.1.0 або пізнішої версії ...

У вас не буде такої опції, як опція sed, -iтому замість цього виконайте:

$ awk '{print $0}' file > tmp && mv tmp file

Зауважте: -iце не магія, воно також створює тимчасовий файл, sedпросто обробляє його для вас.


Станом на GNU awk 4.1.0 ...

GNU awkдодав цю функціональність у версії 4.1.0 (випущена 05.05.2013) . Це не так прямо вперед, як просто надання -iопції, як описано у випущених примітках:

Нова опція -i (від xgawk) використовується для завантаження файлів бібліотеки awk. Це відрізняється від -f тим, що перший аргумент, що не є варіантом, трактується як сценарій.

Потрібно використовувати пакетний inplace.awkфайл включення, щоб викликати розширення належним чином так:

$ cat file
123 abc
456 def
789 hij

$ gawk -i inplace '{print $1}' file

$ cat file
123
456
789

Змінна INPLACE_SUFFIXможе бути використана для визначення розширення для файлу резервної копії:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{print $1}' file

$ cat file
123
456
789

$ cat file.bak
123 abc
456 def
789 hij

Я щасливий, що ця функція була додана, але для мене реалізація не дуже хитра, оскільки влада виходить з лаконічності мови і -i inplaceмає 8 символів занадто довгий imo .

Ось посилання на посібник для офіційного слова.


Чи не повинен ваш "перший" приклад більше нагадувати awk '{ gsub(/foo/, "bar" ) } ; { print $0 }' file > tmp.txt && mv -v tmp.txt file:?
Тоні Барганскі

На мій подив, станом на квітень 2019 року, все ще на гаук 4.0.2. Не дозволяйте нікому говорити вам таке, і така версія буде доступна.
Джон

Litte коротше, awk '{print $0}' file | sponge fileвикористовуючи spongeвід moreutils.
brablc

15

@sudo_O має правильну відповідь .

Це не може працювати:

someprocess < file > file

Оболонка виконує перенаправлення перед передачею управління деякомупроцесу ( перенаправлення ). >Перенаправлення буде обрізає файл до нульового розміру ( вихідний перенаправлення ). Отже, до моменту запуску деякогопроцесу та його хотіти прочитати з файлу, для його читання немає даних.


14

просто невеликий хак, який працює

echo "$(awk '{awk code}' file)" > file

Працює як шарм! Але чи можна зберегти команду awk у змінну і просто використати її у своєму вишуканому трюку?
ашрасмун

13

Альтернативою є використання sponge:

awk '{print $0}' your_file | sponge your_file

Там, де ви замінюєте '{print $0}'свій скрипт awk та your_fileім'ям файлу, який ви хочете відредагувати.

sponge повністю поглинає вхід, перш ніж зберегти його у файл.


Наскільки стандартна / портативна губка?
Томас

2
spongeє частиною moreutils. Таким чином, він не буде присутній за замовчуванням у більшості систем. Але схоже на те, що принаймні spongeсама є достатньо портативною і її можна працювати майже скрізь.
MarSoft

1
Мінусом цього рішення в порівнянні з tee-based є те, що spongeвін прочитає все в оперативній пам'яті перед записом, отже, він застигне на великих файлах.
MarSoft

5

наступне не буде працювати

echo $(awk '{awk code}' file) > file

це має працювати

echo "$(awk '{awk code}' file)" > file

3

У випадку, якщо ви хочете розробити лише проблему без створення тимчасового файлу та застосованого у версії! = (Gawk 4.1.0):

awk '{a[b++]=$0} END {for(c=0;c<=b;c++)print a[c]>ARGV[1]}' file

4
Але чи буферизує цей файл весь пам'ять? Розглянемо файл на 20 ГБ.
Аміт Найду

0

Використання трійника

 awk '{awk code}' file | tee file

teeкоманди мають місце і виконуються після awkзавершення команди з - за |.


5
Це неправильно. Дві команди виконуються паралельно, і дані негайно передаються через трубу. Будь-який файл, більший за буфер (8192 байти на моїй машині), буде усічений, і ви втратите дані.
tripflag
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.