Подтвердить что ты не робот

Как я могу использовать файл в команде и перенаправить вывод в тот же файл без его усечения?

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

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

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

4b9b3361

Ответ 1

Вы не можете сделать это, потому что 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.

Ответ 2

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

Попробуйте выполнить следующую команду:

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

Ответ 3

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

sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name

Ответ 4

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

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

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

Ответ 5

Вы не можете использовать оператор перенаправления (> или >>) для одного и того же файла, потому что он имеет более высокий приоритет, и он создаст/урежет файл до того, как команда будет даже вызвана. Чтобы избежать этого, вы должны использовать соответствующие инструменты, такие как 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 - команды execute :write и :quit

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

Ответ 6

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

VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name

Ответ 7

Там также 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

Ответ 8

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

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

Пример

Ответ 9

Вы можете сделать это с помощью 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 записывает вывод

Ответ 10

Поскольку этот вопрос является главным результатом в поисковых системах, здесь один-liner из 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 ГБ).

Я взял этот ответ от Харта Симха и Кос.

Ответ 11

Обычно я использую программу tee для этого:

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

Он создает и удаляет временный файл сам по себе.

Ответ 12

Попробуй это

echo -e "AAA\nBBB\nCCC" > testfile

cat testfile
AAA
BBB
CCC

echo "$(grep -v 'AAA' testfile)" > testfile
cat testfile
BBB
CCC

Ответ 13

Далее будет сделать то же самое, что sponge делает, не требуя moreutils:

    shuf --output=file --random-source=/dev/zero 

--random-source=/dev/zero part 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"
}

Ответ 14

возможно, вы можете сделать это вот так:

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