Если вы правильно рассчитываете время своих шагов, это может быть довольно легко. Наиболее важно получить буфер исходного файла, который не взорвется при перегрузке. Единственный реальный способ сделать это - использовать другой файл, и оболочка делает это очень легко.
{ head -n "$((num_lines_before_insert))"
grep key temp_file; sed \$d
} <desired.txt
$( cat
Итак, для большинства оболочек (включая bash
и zsh
, но не тире
или yash
) , когда вы получаете here_document, оболочка создает временный файл с уникальным именем в
$ {TMPDIR: - / tmp}
, exec
s это на дескриптор входного файла, который вы указываете - (или, по умолчанию, просто 0) - и незамедлительно удаляет его. К тому времени, когда он используется в качестве входных данных для вашей команды, это файл un_ named - он не имеет оставшихся ссылок на какую-либо файловую систему и просто ожидает, пока ядро очистит его, прежде чем он полностью исчезнет. Это правильный файл - его данные существуют где-то на диске (или, по крайней мере, в VFS в вероятном случае tmpfs
) , и ядро будет гарантировать, что он продолжит делать это в по крайней мере, пока вы не освободите дескриптор файла.
Таким образом - до тех пор, пока ваша оболочка получает фактический файл поддержки для heredoc - они представляют собой очень безопасные и простые средства обработки временных файлов, поскольку они полностью записаны, и все имена файловых систем уже удалены из них раньше, чем когда-либо. вы их читаете. Таким образом, их данные не могут быть изменены во время работы.
Вышеупомянутый блок сначала записывает временный файл с помощью cat
и сохраняет любые / все завершающие пустые строки из подстановки команды с помощью echo
- который добавляет одну строку в хвост файла. Из оператора {
составной команды }
вывод трех его команд записывается в желаемый.txt
- две из которых, в свою очередь, читают из наследственного документа заголовок
и конец исходного файла - и команда grep
, которая вставляет совпадение с вашим ключом
.
Я не уверен, нужно ли вам это, но я подумал, что уместно показать, что вы можете просто и безопасно полностью перезаписать исходный файл такой последовательностью.
Если ваша оболочка не получает фактический файл для heredocs, вы можете эмулировать то, что она делает ...
{ set "$$" "${TMPDIR:-/tmp}" "$@"
exec <"$2/$( set -C
>"$2/$1" cat &&
echo "$1")" >&1
rm -- "$2/$1";shift 2
head "-n$((before))"
grep ... keyfile; cat
} source_file
... что обеспечит возможность записи всех файлов и их безопасное присвоение файлу -дескрипторы перед выполнением каких-либо необратимых действий, но также выполняет очистку всей файловой системы до того, как делает то же самое.
Вот тест, который я провел, чтобы продемонстрировать это:
cd /tmp
set "$$" "${TMPDIR:-/tmp}" "$@"
seq 5000000 >test
printf line\ %s\\n 1 2 3 4 5 >test2
{ exec <"$2/$( set -C
>"$2/$1" cat &&
echo "$1")" >&1
rm -- "$2/$1";shift 2
head -n2500000
grep 3 test2;cat
} test
Сначала были созданы два файла - один с именем / tmp / test
, который состоял всего из 5 миллионов пронумерованных строк, как написано в seq
] и второй под названием / tmp / test2
, который состоял всего из 5 строк вроде ...
line 1
line 2
line 3
line 4
line 5
Затем я запустил указанный выше блок, затем я сделал ...
sed -n '1p;$p;2499999,2500002l'
...что, что интересно, заняло практически столько же времени, что и операция вставки, и напечатано:
1
2499999$
2500000$
line 3$
2500001$
5000000
5000001 test
Вот как это работает:
1
важно - оно устанавливает O_RDWR в stdout и гарантирует, что при записи в файл каждый процесс перезаписывает предыдущее содержимое файла. Другими словами, это означает, что исходный / целевой файл никогда не усекается, а скорее переписывается с головы до пят. exec
выполняет часть racy как можно скорее (или как только я знаю) . В подгруппе команд noclobber установлен
, поэтому, если «$ {TMPDIR: - / tmp} / $$»
уже существует, раскрытие результатов в exec , который в интерактивной оболочке сразу же остановит весь процесс, или, в сценарии, вызовет завершение сценария со значимой ошибкой, поскольку оболочка не может exec
каталог как стандартный ввод.
cat
копирует исходный_файл
во временный файл, который еще не существует, а echo
записывает имя в стандартный вывод. exec
ed rm
unlink ()
, они станут новым временным файлом, так что его единственное мимолетное утверждение о существовании сейчас - только что назначенное перенаправление.
head
перебирает строки размером 2,5 мил и записывает первые строки размером 2,5 мил source_file
. Дело в том, чтобы искать в обоих файлах равные смещения.
head
читается из файла на диске и записывается в файл в RAM. exec "$ (... head ... & 0
, чтобы сделать файл tmp доступным для чтения / записи и, возможно, использовать head
/ tail
с указанным количеством строк в конце. В этом случае число даже не обязательно должно быть точным - вы можете перебрать в цикле ввод аналогичным образом - увеличивая смещение только немного за раз. Встроенная оболочка read
может использоваться для проверки EOF - или wc
может использоваться при разомкнутом цикле. cat
, вероятно, просто зависнет на
stdin, потому что он никогда не увидит EOF. grep
считывает некоторые данные из другого файла и записывает их в исходный_файл
, перезаписывая только то количество байтов, которое было прочитано из другого места. cat
исправляет любое несоответствие grep
, которое могло быть вызвано только записью того, что осталось от его stdin, в его stdout 1 source_file
.