Главная » Программирование для UNIX » Замена файла: команда overwrite

0

У команды sort есть параметр –o для перезаписи файла:

$ sort file1  -o  file2

Эта запись эквивалентна следующей:

$ sort file1  >file2

Если  file1  и  file2 –  это  один  и  тот  же  файл,  то  перенаправление > очистит входной файл, не выполняя сортировку. А параметр –o работает корректно, т. к. входные данные сортируются и сохраняются во временном файле до того, как создается выходной файл.

Многие  другие команды также могли бы  использовать параметр –o.

Например, sed могла бы редактировать файл прямо на месте:

$ sed  ‘s/UNIX/UNIX(TM)/g’  ch2 -o  ch2         Так не работает!

Было бы непрактично изменять все подобные команды так, чтобы мож но было  добавить этот параметр. Более  того, это вообще  плохая идея: лучше централизовать функции, как делает оболочка при помощи оператора >. Давайте напишем программу overwrite, которая будет выполнять эту работу. Пример, приведенный выше, будет выглядеть так:

$ sed  ‘s/UNIX/UNIX(TM)/g’  ch2 | overwrite ch2

Основная мысль весьма незатейлива – просто сохранять входные данные, пока  не  будет  достигнут конец файла, затем скопировать их  в указанный файл:

# overwrite:  копировать стандартный ввод в вывод  после EOF

#  версия  1.  С  ОШИБКОЙ PATH=/bin:/usr/bin

case  $# in

1)    ;;

*)    echo ‘Usage:  overwrite file’  1>&2;  exit 2 esac

new=/tmp/overwr.$$

trap ‘rm  –f  $new; exit 1′  1 2 15

cat  >$new           # собрать входные данные

cp $new $1           #  перезаписать  входной файл rm –f  $new

Команда cp использована вместо  mv для  того, чтобы  права и владелец выходного файла (если он уже существовал) не изменились.

В этой  заманчиво простой версии сделана  роковая ошибка: если  во время выполнения cp пользователь нажмет клавишу Del, то исходный входной файл будет  испорчен. Необходимо  предотвратить остановку перезаписи входного файла, вызываемую прерыванием:

# overwrite:  копировать стандартный ввод в вывод  после EOF

#  версия  2. И  здесь  есть  ОШИБКА PATH=/bin:/usr/bin

case  $# in

1)    ;;

*)    echo ‘Usage:  overwrite file’  1>&2;  exit 2 esac

new=/tmp/overwr1.$$ old=/tmp/overwr2.$$

trap ‘rm  –f  $new $old; exit 1′ 1 2 15

cat  >$new                   # получить входные  данные

cp $1 $old                   # сохранить исходный  файл

trap ” 1 2 15           # до  завершения игнорировать сигналы cp $new $1                  #  перезаписать  входной файл

rm –f  $new $old

Если  клавиша Del  нажата до начала обработки  исходного файла, то временные файлы удаляются, и с файлом ничего не происходит. После  создания резервной копии сигналы игнорируются, поэтому последняя команда cp не будет прервана и файл будет перезаписан полностью.

Все еще остается небольшая проблема. Рассмотрим

$ sed  ‘s/UNIX/UNIX(TM)g’ precious | overwrite  precious

command  garbled: s/UNIX/UNIX(TM)g

$ ls -l precious

–rw–rw–rw–  1 you                    0 Oct    1 09:02  precious        #$%@*!

$

Если  в  программе, снабжающей overwrite  входными данными, есть ошибка, то ее вывод будет пустым, и надежная overwrite, как ей и положено, уничтожит данные во входном файле.

Можно предложить несколько решений.  Например, overwrite может запрашивать подтверждение на замену файла, но если сделать эту команду интерактивной, многие ее преимущества будут утрачены. Можно сделать так, чтобы overwrite проверяла свои входные данные на «непустоту» (посредством test  –z), но это некрасиво и неправильно, к тому же какая-то часть  выходных данных уже  может быть сгенерирована к моменту обнаружения ошибки.

Лучшим решением является запуск  программы,  генерирующей данные, под контролем overwrite – так, чтобы  можно было проверять код завершения.  Традиции и  наша  интуиция  выступают против такого подхода. В  конвейере  overwrite обычно   находится  в  конце.  Но  она должна быть первой, т. к. иначе не будет работать правильно. Команда overwrite ничего не направляет в стандартный вывод, так что универсальность сохраняется. А ее синтаксис не такой уж  необычный: time, nice, nohup – все эти команды воспринимают другие команды в качестве аргументов.

Вот безопасная версия программы:

# overwrite:  копировать стандартный ввод в вывод  после EOF

# окончательная версия

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     # очистить  файлы

if PATH=$opath  "$@"  >$new                   #  получить входные  данные then

else fi

cp $file  $old             # сохранить исходный  файл

trap ” 1 2 15           # до  завершения игнорировать сигналы cp $new $file

echo "overwrite: $1  failed, $file unchanged"  1>&2 exit 1

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, легко создать командный файл, автоматизирующий выполнение данной задачи:

$ cat  replace

# replace: заменить str1 в файлах на str2

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; они предназначены для этого.

Упражнение 5.17.  Почему overwrite не использует сигнал 0 в trap, чтобы удалять файлы при  выходе? Подсказка: попробуйте ввести DEL во время выполнения следующей программы:

trap  "echo  exiting;  exit  1"  0 2 sleep  10

~

Упражнение 5.18.  Добавьте параметр –v для  команды replace, чтобы  выводить все изменившиеся строки в /dev/tty. Хорошая подсказка: s/$left/$right/g$vflag. ~

Упражнение 5.19.  Настройте replace так, чтобы она работала вне зависимости от символов строки замены. ~

Упражнение 5.20.  Может ли  команда replace быть  использована для  замены переменной i на index везде в программе? Что можно изменить для того, чтобы удалось решить эту задачу? ~

Упражнение 5.21.  Подходит ли replace и достаточно ли она мощна для  того, чтобы быть внесенной в каталог /usr/bin? Не лучше ли просто вводить соответствующие sed команды в случае необходимости? Почему? ~

Упражнение 5.22 (сложное).

$ overwrite file ‘who | sort’

не работает. Объясните, почему не работает, и исправьте. Подсказка: см.  eval  в sh(1). Как предложенное решение повлияет на интерпретацию метасимволов в команде? ~

Источник: Керниган Б., Пайк Р., UNIX. Программное окружение. – Пер. с англ. – СПб: Символ-Плюс, 2003. – 416 с., ил.

По теме:

  • Комментарии