Во избежание такого финала можно предложить несколько решений. Команда overwrite
test -2), но это некрасиво и к тому же неверно: выходной поток мог быть создан до обнаружения ошибки.Наилучшее решение заключается в том, чтобы выполнять программу, поставляющую данные, под контролем команды overwrite
overwrite обычно должна быть последней, но для правильной работы она должна идти первой. Однако overwrite ничего не выдает в стандартный выходной поток, поэтому можно считать, что не происходит потери общности. Более того, ее синтаксис не является каким-то необычным: time, nice, nohup представляют собой команды, аргументами которых служат другие команды. Ниже приведен безопасный вариант:# overwrite: copy standard input to output after EOF
# final version
opath=$PATH
PATH=/bin:/usr/bin
case $# in
0|1) echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2
esac
file=$1; shift
new=/tmp/overwr1.$$; old=/tmp/overwr2.$$
trap 'rm -f $new $old; exit 1' 1 2 15 # clean up files
if PATH=$opath "$@" >$new # collect input
then
cp $file $old # save original file
trap '' 1 2 15 # we are committed; ignore signals
cp $new $file
else
echo "overwrite: $1 failed, $file unchanged" 1>&2 exit 1
fi
rm -f $new $old
Встроенная команда интерпретатора shift
$2 становится $1, $3 становится $2 и т.д. Строка обозначает все аргументы (после shift), как и $*, но без интерпретации; мы вернемся к ее рассмотрению в разд. 5.7.Заметьте, что значение PATH
/bin или /usr/bin, будут недоступны для overwrite.Теперь команда overwrite
$ cat notice
Unix is a Trademark of Bell Laboratories
$ overwrite notice sed 's/UNIXUNIX(TM)/g' notice
command garbled: s/UNIXUNIX(TM)/g
overwrite: sed failed, notice unchanged
$ cat notice
UNIX is a Trademark of Bell Laboratories
$ overwrite notice sed 's/UNIX/UNIX(TM)/g' notice
$ cat notice
UNIX(TM) is a Trademark of Bell Laboratories
$
Типичной задачей является использование редактора sed
overwrite, легко написать программу на языке shell для ее решения:$ cat replace
# replace: replace str1 in files with str2, in place
PATH=/bin:/usr/bin
case $# in
0|1|2) echo 'Usage: replace str1 str2 files' 1>&2; exit 1
esac
left="$1"; right="$2"; shift; shift
for i do
overwrite $i sed "s@$left@$right@g" $i
done
$ cat footnote
UNIX is not an acronym
$ replace UNIX Unix footnote
$ cat footnote
Unix is not an acronym
$
(Вспомните: если список в цикле for
$*.) Мы использовали @ вместо / для разбиения в команде подстановки, поскольку менее вероятно, что @ вступит в конфликт с входной строкой. Команда replace устанавливает PATH равным /bin:/usr/bin, исключая $HOME/bin. Это означает, что overwrite должна находиться в /usr/bin, чтобы команда replace сработала. Мы сделали такое предположение для простоты; если вы не можете поместить overwrite в /usr/bin, вам придется добавить $HOME/bin к PATH в команде replace или явно задать полное имя overwrite. В дальнейшем будем полагать, что команды, которые мы создаем, находятся в /usr/bin, где им и следует быть.