Как я могу использовать файл в команде и перенаправить вывод в тот же файл, не обрезая его? - PullRequest
77 голосов
/ 14 июля 2011

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

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name

Однако, когда я делаю это, я получаю пустой файл.Есть мысли?

Ответы [ 13 ]

75 голосов
/ 14 июля 2011

Используйте губка для такого рода задач.Его часть moreutils.

Попробуйте эту команду:

 grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | sponge file_name
71 голосов
/ 14 июля 2011

Вы не можете сделать это, потому что bash сначала обрабатывает перенаправления, а затем выполняет команду.Таким образом, к тому времени, как grep просматривает имя_файла, оно уже пусто.Вы можете использовать временный файл.

#!/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.

17 голосов
/ 14 июля 2011

Используйте sed вместо:

sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name
9 голосов
/ 03 июля 2016

попробуйте этот простой

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name

В этот раз ваш файл не будет пустым :), и ваш вывод также будет распечатан на вашем терминале.

7 голосов
/ 18 апреля 2016

Нельзя использовать оператор перенаправления (> или >>) для одного и того же файла, поскольку он имеет более высокий приоритет и создает / усекает файл до того, как команда даже будет вызвана.Чтобы избежать этого, вы должны использовать соответствующие инструменты, такие как 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 / Vim
  • g/pattern/d - удалить строки, соответствующие шаблону, используя global (help :g)
  • -s - беззвучный режим (man ex)
  • -c wq - выполнить :write и :quit команды

Вы можете использовать sed для достиженияТо же самое (как уже показано в других ответах), однако на месте (-i) является нестандартным расширением FreeBSD (может работать по-разному в Unix / Linux) и в основном это s tream ed itor, а не редактор файлов.См .: Имеет ли Ex режим какое-либо практическое применение?

3 голосов
/ 25 октября 2013

Один вариант с вкладышем - установите содержимое файла как переменную:

VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name
2 голосов
/ 18 сентября 2018

Так как этот вопрос является лучшим результатом в поисковых системах, вот строчка из 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

Тест от https://askubuntu.com/a/752451:

printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do echo "$(cat file_uniquely_named.txt)" > file_uniquely_named.txt; done; cat file_uniquely_named.txt; rm file_uniquely_named.txt

Следует напечатать:

hello
world

Принимая во внимание вызов 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 ГБ).

Я позаимствовал этот ответ у Харта Симхи и kos .

2 голосов
/ 03 ноября 2016

Вы можете сделать это, используя process-substitution .

Это что-то вроде хака, поскольку bash открывает все каналы асинхронно, и мы должны обойти это, используя sleep, поэтому YMMV.

В вашем примере:

grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name)
  • >(sleep 1 && cat > file_name) создает временный файл, который получает выходные данные от grep
  • sleep 1 задержка на секунду, чтобы дать grep время для анализа входного файла
  • наконец cat > file_name записывает вывод
2 голосов
/ 11 мая 2014

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

!/seg[0-9]\{1,\}\.[0-9]\{1\}/ {
  q = q ? q RS $0 : $0
}
END {
  print q > ARGV[1]
}

Пример

2 голосов
/ 14 июля 2011

Существует также 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...