Могу ли я сделать `cut` изменить файл на месте?

Ответы:

14

Ты не можешь Либо используйте ed или GNU sed или perl, либо делайте то, что они делают за кулисами, то есть для создания нового файла для содержимого.

edпортативный:

ed foo <<EOF
1,$s/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/
w
q
EOF

GNU sed:

sed -i -e 's/^\([^,]*\),\([^,]*\),\([^,]*\).*/\1,\3/' foo

Perl:

perl -i -l -F, -pae 'print @F[1,3]' foo

cut, создание нового файла (рекомендуется, потому что если ваш скрипт прерван, вы можете просто запустить его снова):

cut -d , -f 1,3 <foo >foo.new &&
mv -f foo.new foo

cut, заменив файл на месте (сохраняет право собственности и права доступа foo, но нуждается в защите от прерываний):

cp -f foo foo.old &&
cut -d , -f 1,3 <foo.old >foo &&
rm foo.old

Я рекомендую использовать один из cutметодов на основе. Таким образом, вы не зависите от какого-либо нестандартного инструмента, вы можете использовать лучший инструмент для работы и контролировать поведение при прерывании.

Жиль "ТАК - перестань быть злым"
источник
Лучше, чем .oldметод для изменений на месте,echo "$(cut -d , -f 1,3 <foo)" > foo
GypsyCosmonaut
1
@GypsyCosmonaut Нет, это не «лучше». Это более хрупкий. Его единственное преимущество в том, что он короче набирает текст. Основная проблема вашего метода заключается в том, что если при обработке входного файла или записи выходных данных произойдет ошибка, данные будут потеряны. Он также не работает с двоичными данными: вывод будет обрезан до первого нулевого байта. Даже с текстовыми файлами, он удаляет пустые строки из конца файла. В случае больших файлов это может привести к сбою, поскольку данные должны храниться в виде строки в памяти оболочки (и помните, что если это произойдет, данные будут потеряны).
Жиль "ТАК ... перестать быть злым"
oO Спасибо, я не знал, что могут быть проблемы с нулевым байтом
GypsyCosmonaut,
Я думаю, что это проще:cut -d , -f 1,3 foo > foo.new rm foo mv foo.new foo
LoMaPh
@LoMaPh Действительно, я не знаю, почему я переименовал старый файл: у него нет никаких преимуществ по сравнению с переименованием нового файла. Это также проще, потому что вам не нужен шаг rm foo. И вам не следует звонить rm foo, потому что mv foo.new fooэто атомарно: оно удаляет старую версию и одновременно устанавливает новую версию на место.
Жиль "ТАК - перестань быть злым"
23

В пакете moreutils из ubuntu (а также из Debian ) есть программа sponge, которая также решает вашу проблему.

От мужского губка:

Губка читает стандартный ввод и записывает его в указанный файл. В отличие от перенаправления оболочки, губка впитывает весь свой ввод перед открытием выходного файла. Это позволяет сжимать конвейеры, которые читают и записывают в один и тот же файл.

Что позволит вам сделать что-то вроде:

cut -d <delim> -f <fields> somefile | sponge somefile
Кжетил Йоргенсен
источник
9

Я не думаю, что это возможно, используя в cutодиночку. Я не мог найти это на странице человека или информации. Вы можете сделать что-то такое, как

mytemp=$(mktemp) && cut -d" " -f1 file > $mytemp && mv $mytemp file

mktempделает вас относительно безопасным временным файлом, в который вы можете перенаправить cutвывод.

Стивен Д
источник
1

Попробуйте vim-way:

$ ex -s +'%!cut -c 1-10' -cxa file.txt

Это отредактирует файл на месте (сначала сделайте резервную копию).

В качестве альтернативы используют grep, sedили gawk.

kenorb
источник
0

Вы можете использовать slurp с POSIX Awk:

cut -b1 file | awk 'BEGIN{RS="";getline<"-";print>ARGV[1]}' file

пример

Стивен Пенни
источник
0

Ну, поскольку cutвыдает меньше информации, чем читает, вы можете сделать:

cut -c1 < file 1<> file

То есть, сделать его стандартный fileвывод fileоткрытым в режиме только для чтения, а стандартный вывод - открытым в режиме чтения + записи без усечения ( <>).

Таким образом, cutфайл будет перезаписан поверх самого себя. Тем не менее, оставшаяся часть файла останется нетронутой. Например, если fileсодержит:

foo
bar

На выходе получится:

f
b
bar

f\nb\nЗаменили foo\n, но barвсе же есть. Вам нужно будет обрезать файл послеcut завершения.

С помощью ksh93этого вы можете сделать это с помощью <>;оператора, который действует так, <>за исключением того, что, если команда выполнена успешно, ftruncate()вызывается дескриптор файла. Так:

cut -c1 < file 1<>; file

С другими оболочками вам нужно будет сделать это с ftruncate()помощью других средств, таких как:

{ cut -c1 < file; perl -e 'truncate STDOUT, tell STDOUT';} 1<> file

хотя вызывать perlтолько для этого здесь немного излишне, особенно если учесть, что perlэто легко может сделать cutтакую ​​работу, как:

perl -pi -e '$_ = substr($_, 0, 1)' file

Помните, что со всеми методами, которые включают фактическую перезапись на месте, если операция прерывается на полпути, вы получите поврежденный файл. Использование временного второго файла позволяет избежать этой проблемы.

Стефан Шазелас
источник