Хорошо известно, что такая команда:
cat filename | some_sed_command >filename
стирает имя файла, так как перенаправление вывода, выполняемое перед командой, вызывает усечение имени файла.
Можно решить проблему следующим образом:
cat file | some_sed_command | tee file >/dev/null
, но я не уверен, что это сработает в любом случае: что произойдет, если файл (и результат команды sed) очень большой? Как операционная система может избежать перезаписи содержимого, которое до сих пор не прочитано? Я вижу, что есть также команда губки, которая должна работать в любом случае: она "безопаснее", чем тройник?
можно было решить проблему следующим образом:
cat file | some_sed_command | tee file >/dev/null
Никакой .
возможности file
будут усеченным отбрасыванием, но нет никакой гарантии cat file | some_sed_command | tee file >/dev/null
, не усечет file
.
Все это зависит, на котором команда обрабатывается сначала, как в противоположность тому, что можно ожидать, , команды в канале не обрабатываются слева направо . Нет никакой гарантии, о которой команда будет выбрана сначала, таким образом, можно было бы также просто думать о ней, как случайным образом выбрано, и никогда не полагаются на оболочку, не выбирающую нарушающую.
Начиная с возможностей для незаконной команды, которая будет выбрана сначала, промежуточные три команды ниже, чем возможности для незаконной команды, которая будет выбрана сначала промежуточные две команды, менее вероятно, что file
будет усеченным, но она все еще собирается произойти .
script.sh
:
#!/bin/bash
for ((i=0; i<100; i++)); do
cat >file <<-EOF
foo
bar
EOF
cat file |
sed 's/bar/baz/' |
tee file >/dev/null
[ -s file ] &&
echo 'Not truncated' ||
echo 'Truncated'
done |
sort |
uniq -c
rm file
% bash script.sh
93 Not truncated
7 Truncated
% bash script.sh
98 Not truncated
2 Truncated
% bash script.sh
100 Not truncated
Так никогда использование что-то как cat file | some_sed_command | tee file >/dev/null
. Используйте sponge
в качестве предложенного Oli.
Как альтернатива, для thighter сред и / или относительно маленькие файлы можно использовать здесь строка и замена команды для чтения файла, прежде чем любая команда будет выполнена:
$ cat file
foo
bar
$ for ((i=0; i<100; i++)); do <<<"$(<file)" sed 's/bar/baz/' >file; done
$ cat file
foo
baz
Можно использовать Vim в режиме Ex:
ex -sc '%!some_sed_command' -cx filename
%
выбор все строки
!
Команда выполнения
x
Сохраняет и вышла
Для sed
а именно, можно использовать -i
оперативный аргумент. Это просто сохраняет назад в файл, который это открыло, например:
sed -i 's/ /-/g' filename
, Если Вы хотите сделать что-то более раскормленное, предполагая выполнение больше чем sed
, да, можно буферизовать все это с sponge
(от moreutils
пакет), который "впитает" весь stdin перед выписыванием в файл. Это похоже tee
, но с меньшей функциональностью. Для основного использования, хотя, это - в значительной степени общедоступная замена:
cat file | some_sed_command | sponge file >/dev/null
то, что более безопасен? Определенно. Это, вероятно, имеет пределы через поэтому, если Вы делаете что-то колоссальное (и не может оперативное редактирование с sed), Вы могли бы хотеть сделать свои редактирования во второй файл и затем mv
что файл назад к исходному имени файла. Это должно быть атомарным (таким образом, что-либо в зависимости от этих файлов не повредится, если им будет нужен постоянный доступ).
О, но sponge
не единственная опция; Вы не должны получать moreutils
, чтобы заставить это работать правильно. Любой механизм будет работать, пока он удовлетворяет следующие два требования:
Вы видите, известная проблема, к которой относится OP, состоит в том, что оболочка создаст все файлы, которые необходимы, чтобы каналы работали перед ровным началом выполнить команды в конвейере, таким образом, это - оболочка, которая на самом деле усекает выходной файл (который, к сожалению, является также входным файлом), прежде чем любая из команд даже имела шанс начать выполняться.
Эти tee
команда не работает, даже при том, что она удовлетворяет первое требование, потому что она не удовлетворяет второе требование: это будет всегда создавать выходной файл непосредственно после запуска, таким образом, это по существу настолько же плохо как создаст канал прямо в выходной файл. (Это на самом деле хуже, потому что его использование представляет недетерминированную случайную задержку, прежде чем выходной файл станет усеченным, таким образом, Вы могли бы думать, что это работает, в то время как на самом деле это не делает.)
Так, все нам нужно для решения этой проблемы, некоторая команда, которая буферизует весь ее вход прежде, чем произвести любой вывод, и это способно к принятию выходного имени файла в качестве параметра, так, чтобы мы не передавали его вывод по каналу в выходной файл. Одна такая команда shuf
. Так, следующее выполнит то же самое, которое sponge
делает:
shuf --output=file --random-source=/dev/zero
--random-source=/dev/zero
приемы части shuf
в выполнение ее вещи, не делая никакой перестановки вообще, таким образом, это буферизует Ваш вход, не изменяя его.