Сохраняйте изменения на месте с помощью awk

135

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

Я понимаю, что могу использовать перенаправление для записи изменений. Однако есть ли возможность awkэто сделать?

Дино
источник
Также см. Serverfault.com/a/547331/313521 для более общего ответа на «редактирование файла на месте с перенаправлением».
Wildcard
@Wildcard. Решение там ужасно хрупкое. Нет абсолютно никакой гарантии относительно порядка событий, и использование этого решения может привести к сокращению ваших данных. Кстати, я не могу комментировать этот сайт напрямую, потому что для этого мне нужно 50 представителей на этом сайте. Я никогда не пойму, почему SO раздроблен на Unix / Linux и администратора сервера и др. ИМО, это была ошибка.
Уильям Перселл
@WilliamPursell, «нет гарантии порядка событий» - что на самом деле неверно. Единственная уязвимость этого решения заключается в том, что длина содержимого превышает максимальную длину команды. Однако порядок событий гарантирован.
Wildcard
@Wildcard Какой стандарт гарантирует такой заказ?
Уильям Перселл,
@WilliamPursell это гарантировано документацией bash. Для других снарядов не знаю. (Между прочим, если вы свяжете свою учетную запись, у вас будет 100 бонусов ассоциации репутации и вы сможете комментировать.)
Wildcard

Ответы:

142

В последней версии GNU Awk (начиная с версии 4.1.0 ) есть возможность редактирования файла "на месте" :

[...] Расширение "inplace", построенное с использованием нового средства, может использоваться для имитации " sed -i" возможности GNU . [...]

Пример использования:

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

Чтобы сохранить резервную копию:

$ gawk -i inplace -v INPLACE_SUFFIX=.bak '{ gsub(/foo/, "bar") }
> { print }' file1 file2 file3
Lind
источник
1
@sudo_O - Спасибо за демонстрацию "на месте". Проголосовали за ваш ответ!
lind
Похоже, опцию убрали? В версии 4.1.3 у меня есть «-i includefile --include = includefile»
Кейт Хьюитт,
1
@Keith У меня был такой же вопрос. Я только что попробовал, и он работает на моем 4.1.3. inplaceна самом деле является библиотекой, включенной в gawkсоответствии с ответом iiSeymour , поэтому inplaceэто то, что может быть включено как файлincludefile .
cxw
Важное предостережение: массив 'visible' заполнится повторяющимися строками из ВСЕХ файлов, включенных в команду. Таким образом, если каждый файл имеет, например, общий заголовок, он будет удален в каждом файле после первого. Если вместо этого вы хотите обрабатывать каждый файл независимо, вам нужно сделать что-то вроде f в * .txt; сделать gawk -i inplace '! seen [$ 0] ++' "$ f"; готово
Nick K9
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 (выпущен 10.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

Я счастлив , эта функция была добавлена , но для меня, реализация не очень awkish как сила исходит от лаконичности языка и -i inplace8 символов слишком долго имо .

Вот ссылка на мануал за официальным словом.

Крис Сеймур
источник
Разве ваш «первый» пример не должен быть похож на awk '{ gsub(/foo/, "bar" ) } ; { print $0 }' file > tmp.txt && mv -v tmp.txt file:?
Тони Барганский 03
К моему удивлению, по состоянию на апрель 2019 года все еще используется gawk 4.0.2. Не позволяйте никому говорить вам, что такая-то версия будет доступна.
Джон Лунзер
Короче литте 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 полностью поглощает ввод перед сохранением в файл.

Codoscope
источник
Насколько стандартна / портативна губка?
Томас
2
spongeявляется частью moreutils. Так что в большинстве систем его не будет по умолчанию. Но похоже, что по крайней мере spongeсам по себе достаточно портативен и может работать практически везде.
MarSoft
1
Обратной стороной этого решения по сравнению с tee-based является то, что spongeвсе данные будут считываться в ОЗУ перед записью, следовательно, большие файлы будут зависать.
MarSoft
5

следующее не сработает

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

это должно работать

echo "$(awk '{awk code}' file)" > file
Flowmix Leonsio
источник
3

Если вам нужно решение только для awk без создания временного файла и его можно использовать с версией! = (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завершения команды из - за |.

Shaiki Siegal
источник
5
Это неверно Две команды выполняются параллельно, и данные немедленно передаются по конвейеру. Любой файл, размер которого превышает размер буфера (8192 байта на моей машине), будет усечен, и вы потеряете данные.
tripflag