В основном я хочу взять в качестве входного текста из файла, удалить строку из этого файла и отправить результат обратно в тот же файл. Что-нибудь в этом роде, если это проясняет.
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name
однако, когда я это делаю, я получаю пустой файл. есть идеи?
Ответы:
Вы не можете этого сделать, потому что bash сначала обрабатывает перенаправления, а затем выполняет команду. Итак, к тому времени, когда grep посмотрит на file_name, он уже пуст. Однако вы можете использовать временный файл.
#!/bin/sh tmpfile=$(mktemp) grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > ${tmpfile} cat ${tmpfile} > file_name rm -f ${tmpfile}
таким образом, подумайте об использовании
mktemp
для создания tmpfile, но обратите внимание, что это не POSIX.источник
>
перенаправление откроет файл и усечет его перед запуском оболочкиgrep
.sponge
команды .Используйте губку для такого рода задач. Его часть moreutils.
Попробуйте эту команду:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | sponge file_name
источник
brew install moreutils
.sudo apt-get install moreutils
в системах на основе Debian.Вместо этого используйте sed:
sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name
источник
-i
- это только расширение GNU.-i ''
что расширение не является строго обязательным, но для этой-i
опции требуется некоторый аргумент.попробуйте этот простой
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
На этот раз ваш файл не будет пустым :), и ваш результат также будет распечатан на вашем терминале.
источник
/dev/null
или аналогичные места.Вы не можете использовать оператор перенаправления (
>
или>>
) для того же файла, потому что он имеет более высокий приоритет и будет создавать / усекать файл до того, как команда будет запущена. Чтобы избежать этого, вы должны использовать соответствующие инструменты , такие какtee
,sponge
,sed -i
или любой другой инструмент , который может записывать результаты в файл (напримерsort file -o file
).По сути, перенаправление ввода в тот же исходный файл не имеет смысла, и вы должны использовать для этого соответствующие редакторы на месте, например редактор Ex (часть Vim):
ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name
где:
'+cmd'
/-c
- запустить любую команду Ex / Vimg/pattern/d
- удалить строки, соответствующие шаблону, используя global (help :g
)-s
- беззвучный режим (man ex
)-c wq
- выполнить:write
и:quit
командыВы можете использовать
sed
для достижения того же (как уже было показано , в других ответах), однако на месте (-i
) является нестандартным расширением FreeBSD (может работать по- разному между Unix / Linux) , и в основном это s Tream ред itor, а не редактор файлов . См .: Есть ли практическое применение в режиме Ex?источник
Альтернатива с одним лайнером - установите содержимое файла как переменную:
VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name
источник
Поскольку этот вопрос является лучшим результатом в поисковых системах, вот однострочный запрос на основе https://serverfault.com/a/547331, который использует подоболочку вместо
sponge
(что часто не является частью стандартной установки, такой как OS X) :echo "$(grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name)" > file_name
Общий случай:
echo "$(cat file_name)" > file_name
Изменить, в приведенном выше решении есть некоторые предостережения:
printf '%s' <string>
следует использовать вместо,echo <string>
чтобы файлы, содержащие-n
, не вызывали нежелательного поведения.x
к выходным данным, и удалить его снаружи через расширение параметров временной переменной, например${v%x}
.$v
вытесняет значение любой существующей переменной$v
в текущей среде оболочки, поэтому мы должны вложить все выражение в круглые скобки, чтобы сохранить предыдущее значение.null
из вывода. Я проверил это, позвонивdd if=/dev/zero bs=1 count=1 >> file_name
и просмотрев в шестнадцатеричном формате с помощьюcat file_name | xxd -p
. Ноecho $(cat file_name) | xxd -p
раздевается. Таким образом , как указал Линч, этот ответ не следует использовать для двоичных файлов или чего-либо, содержащего непечатаемые символы .Общее решение (albiet немного медленнее, требует больше памяти и по-прежнему удаляет непечатаемые символы):
(v=$(cat file_name; printf x); printf '%s' ${v%x} > file_name)
Тест с https://askubuntu.com/a/752451 :
printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do (v=$(cat file_uniquely_named.txt; printf x); printf '%s' ${v%x} > file_uniquely_named.txt); done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
Следует напечатать:
В то время как вызов
cat file_uniquely_named.txt > file_uniquely_named.txt
в текущей оболочке:printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do cat file_uniquely_named.txt > file_uniquely_named.txt; done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
Печатает пустую строку.
Я не тестировал это на больших файлах (вероятно, более 2 или 4 ГБ).
Я позаимствовал этот ответ у Харта Симха и Кос .
источник
cat
и помещает его в качестве первого аргументаecho
. Конечно, непечатаемые переменные не будут выводиться должным образом и испортят данные. Не пытайтесь перенаправить файл обратно на себя, это не может быть хорошим.Также есть
ed
(как альтернативаsed -i
):# cf. http://wiki.bash-hackers.org/howto/edit-ed printf '%s\n' H 'g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' wq | ed -s file_name
источник
Вы можете сделать это, используя подстановку процесса .
Это немного похоже на взлом, поскольку bash открывает все каналы асинхронно, и мы должны обойти это, используя
sleep
YMMV.В вашем примере:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name)
>(sleep 1 && cat > file_name)
создает временный файл, который получает вывод от grepsleep 1
задержки на секунду, чтобы дать grep время проанализировать входной файлcat > file_name
записывает выводисточник
Вы можете использовать slurp с POSIX Awk:
!/seg[0-9]\{1,\}\.[0-9]\{1\}/ { q = q ? q RS $0 : $0 } END { print q > ARGV[1] }
пример
источник
Это очень возможно, вам просто нужно убедиться, что к моменту написания вывода вы записываете его в другой файл. Это можно сделать, удалив файл после открытия ему файлового дескриптора, но перед записью в него:
exec 3<file ; rm file; COMMAND <&3 >file ; exec 3>&-
Или построчно, чтобы лучше понять:
exec 3<file # open a file descriptor reading 'file' rm file # remove file (but fd3 will still point to the removed file) COMMAND <&3 >file # run command, with the removed file as input exec 3>&- # close the file descriptor
Это по-прежнему рискованно, потому что, если COMMAND не работает должным образом, вы потеряете содержимое файла. Это можно уменьшить, восстановив файл, если КОМАНДА возвращает ненулевой код выхода:
exec 3<file ; rm file; COMMAND <&3 >file || cat <&3 >file ; exec 3>&-
Мы также можем определить функцию оболочки, чтобы упростить ее использование:
# Usage: replace FILE COMMAND replace() { exec 3<$1 ; rm $1; ${@:2} <&3 >$1 || cat <&3 >$1 ; exec 3>&- }
Пример :
$ echo aaa > test $ replace test tr a b $ cat test bbb
Также обратите внимание, что при этом будет сохранена полная копия исходного файла (до закрытия третьего файлового дескриптора). Если вы используете Linux, и файл, который вы обрабатываете, слишком велик, чтобы дважды поместиться на диске, вы можете проверить этот скрипт, который будет перенаправлять файл в указанную команду блок за блоком, освобождая уже обработанные блоки. Как всегда, прочтите предупреждения на странице использования.
источник
Попробуй это
echo -e "AAA\nBBB\nCCC" > testfile cat testfile AAA BBB CCC echo "$(grep -v 'AAA' testfile)" > testfile cat testfile BBB CCC
источник
Следующее сделает то же самое,
sponge
но не требуетmoreutils
:Эта
--random-source=/dev/zero
часть пытаетсяshuf
делать свое дело, не тасуя вообще, поэтому она буферизует ваш ввод, не изменяя его.Однако это правда, что использование временного файла лучше всего по соображениям производительности. Итак, вот функция, которую я написал, которая сделает это за вас в обобщенном виде:
# Pipes a file into a command, and pipes the output of that command # back into the same file, ensuring that the file is not truncated. # Parameters: # $1: the file. # $2: the command. (With $3... being its arguments.) # See https://stackoverflow.com/a/55655338/773113 function siphon { local tmp=$(mktemp) local file="$1" shift $* < "$file" > "$tmp" mv "$tmp" "$file" }
источник
Для этого я обычно использую программу tee :
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
Он сам создает и удаляет временный файл.
источник
tee
не гарантируется. См. Askubuntu.com/a/752451/335781 .