Я хочу избежать сохранения временных файлов в случае сбоя моей программы.
UNIX хорош тем, что позволяет сохранять файл открытым даже после его удаления.
Так что если вы откроете файл, немедленно удалите его, изатемвыполнять медленную обработку, высоки шансы, что даже если ваша программа даст сбой, пользователю не придется очищать файл.
В оболочке я часто вижу что-то похожее на:
generate-the-file -o the-file
[...loads of other stuff that may use stdout or not...]
do_slow_processing < the-file
rm the-file
Но если программа выйдет из строя раньше, rm
пользователю придется ее очистить the-file
.
В Perl вы можете сделать:
open(my $filehandle, "<", "the-file") || die;
unlink("the-file");
while(<$filehandle>) {
# Do slow processing stuff here
print;
}
close $filehandle;
Затем файл удаляется сразу после открытия.
Есть ли подобная конструкция в shell?
решение1
Это протестировано в csh, tcsh, sh, ksh, zsh, bash, ash, sash:
echo foo > the-file
(rm the-file; cat) < the-file | do_slow_processing
do_other_stuff
или, если вы предпочитаете:
(rm the-file; do_slow_processing) < the-file
do_other_stuff
Интересно, что это также работает для fifos:
mkfifo the-fifo
(rm the-fifo; cat) < the-fifo | do_slow_processing &
echo foo > the-fifo
Это происходит потому, что читатель заблокирован до тех пор, пока что-то не будет написано.
решение2
generate-the-file > the-file
exec 5< the-file
rm the-file
...
do_slow_processing 0<&5
Примечания:
- Вам нужно запустить exec без исполняемого файла, так как это повлияет на дескрипторы самой оболочки.
- Доступно только до 9 fd
- Вы можете использовать /proc/self/fd/X, если вам нужно имя файла. Этот интерфейс непереносим между разновидностями UNIX (хотя, возможно, он работает для вас).
- Попытка снова прочитать fd (например, два вызова
cat 0<&5
) не удастся, так как вы находитесь в EOF. Вам нужно будет перемотать его назад или преодолеть его, прочитав через/proc/self/fd/X
- В большинстве случаев, как описано выше, вы можете обойтись без фактического файла, а сделать простое
generate-the-file | do_slow_processing
Обновлять:
OP упоминает, что generate-the-file
может не выводить свой вывод в stdout. Для этого есть несколько идиом:
- Укажите выходной файл
-
. Обычно принимают выходной файл - для обозначения stdout. Этоподтверждено POSIX.1-2017:Правило 13: Для утилит, использующих операнды для представления файлов, которые необходимо открыть для чтения или записи, операнд «-» следует использовать только для обозначения стандартного ввода (или стандартного вывода, если из контекста ясно, что указывается выходной файл) или файла с именем «-».
(для утилит, отличных от тех, где это явно определено, это определяется реализацией, но есть большая вероятность, что это поддерживается вашим generate-the-file
инструментом)
Используйте
/dev/stdout
или/proc/self/fd/1
. Зависит от ОС, см.Насколько переносимы /dev/stdin, /dev/stdout и /dev/stderr?Используйте подстановку процесса. bash позволит вам писать
>(process substitution)
как имя файла. Например:wget -O >(rot13 > вопрос.txt)https://unix.stackexchange.com/q/579535/70346
Это не будет работать на всех оболочках и требует поддержки ОС для имен файлов fd.
решение3
В bash
оболочке очистку можно выполнить с помощью trap
встроенной команды EXIT (в bash
оболочке введите help trap
):
trap 'rm temp-file' EXIT
Эта функция также существует в dash
оболочке, часто именуемой как . sh
в современных дистрибутивах Linux.