Существует еще один вопрос к этому вопросу, который бросается в глаза:
ОП гласит: Но в течение всего 1 oop, этот ли или это l oop находится внутри другого l oop, имя файла может измениться с неготового на готовое. Когда это происходит, я не получаю такую ошибку файла или каталога.
Здесь необходимо задать следующие вопросы:
- Каково происхождение ошибки? Создание перенаправления ввода с несуществующим файлом.
- Что произойдет с l oop, если файл будет переименован или удален? Ничего, это не влияет на l oop.
- Как я могу решить эту проблему? С проверкой на достоверность (см. Ниже) .
1. Источник ошибки:
Ошибка, с которой сталкиваются в OP, является ошибкой, созданной самой bash при попытке создать перенаправление ввода с несуществующим файлом. Это не имеет никакого отношения к while-l oop, представленному в ОП.
$ cat - < "$(mktemp -u)"
bash: /tmp/tmp.qynPrg8Mst: No such file or directory
2. Что происходит с while-l oop, если ввод удаляется / переименовывается?
Предполагается следующее выполнение:
#!/usr/bin/env bash
fname="$(mktemp)"
seq 1 2 > "$fname"
while read -r line; do
echo "$line"
[ -r "$fname" ] && rm "$fname" && ls -l /proc/$$/fd
done < "$fname"
Этот процесс:
- создает временный файл, содержащий 2 строки
- использует время l oop для чтения файла строка за строкой
- на первом цикле l oop, удаляет ввод файл и печатает файловые дескрипторы запущенного процесса
$ ./test.sh
1
total 0
lr-x------ 1 user group 64 Feb 12 14:52 0 -> /tmp/tmp.6oTtotu6Xr (deleted)
l-wx------ 1 user group 64 Feb 12 14:52 1 -> /dev/pts/46
lrwx------ 1 user group 64 Feb 12 14:52 10 -> /dev/pts/46
lrwx------ 1 user group 64 Feb 12 14:52 2 -> /dev/pts/46
lr-x------ 1 user group 64 Feb 12 14:52 255 -> /home/user/tmp/test.sh
2
Что вы заметили, это то, что, хотя файл удаляется во втором цикле, он все равно может прочитать его. С другой стороны, соответствующий файловый дескриптор (0
) все еще существует, но помечен как удаленный. Причина этого указана в [ServerFault]. Что происходит с удаленным файлом, все еще подлежащим перенаправлению на linux?
Если в программе по-прежнему открыт дескриптор файла, файл продолжает существовать. Он исчезает из списка ваших каталогов и выглядит пропавшим, но до тех пор, пока последний дескриптор файла не будет закрыт, файл продолжает расти.
Когда мы переименовываем файл, ничего не происходит, так как изменяется только имя файла. Дескриптор файла будет указывать на новое имя файла, но оно находится на том же иноде диска.
#!/usr/bin/env bash
fname="$(mktemp)"
fname2="$(mktemp)"
echo "$fname $fname2"
seq 1 2 > "$fname"
while read -r line; do
echo "$line"
[ -r "$fname" ] && mv "$fname" "$fname2" && ls -l /proc/$$/fd
done < "$fname"
, который выводит:
/tmp/tmp.3AYt84IJDW /tmp/tmp.JQJGAsUuBz
1
total 0
lr-x------ 1 user group 64 Feb 12 15:01 0 -> /tmp/tmp.JQJGAsUuBz
l-wx------ 1 user group 64 Feb 12 15:01 1 -> /dev/pts/46
lrwx------ 1 user group 64 Feb 12 15:01 10 -> /dev/pts/46
lrwx------ 1 user group 64 Feb 12 15:01 2 -> /dev/pts/46
lr-x------ 1 user group 64 Feb 12 15:01 255 -> /home/user/tmp/test.sh
2
3 , Что это значит для OP?
Два приведенных выше случая демонстрируют, что удаление или переименование файла, который используется в качестве стандартного в процессе, в данном случае while-l oop, не влияет на правильное исполнение л oop. Ошибка возникает только при создании перенаправления ввода с несуществующим файлом. Возможный способ для OP:
#!/usr/bin/env bash
fname="/path/to/input/"
while read -r line
echo "$line"
done < "$( [ -r "$fname" ] && echo "$fname" || echo "/dev/null" )"
Это, однако, все еще делает возможным, но менее вероятно, что входной файл $fname
изменяется во время создания перенаправления ввода.
Самый безопасный способ был бы:
#!/usr/bin/env bash
fname="/path/to/input"
{ exec 3< "$fname"; } 2> /dev/null || { exec 3< /dev/null; }
while read -r -u 3 line
echo "$line"
done
exec 3>&-
Выше по существу задает дескриптор файла 3 для входного файла, но если он не существует, указывает на /dev/null
. Затем он выполняет l oop с /dev/null
Когда у вас, как упомянуто в ОП, есть этот l oop в другой l oop, вы можете сделать:
#!/usr/bin/env bash
fname="/path/to/input"
for i in 1 2; do
{ exec 3< "$fname"; } 2> /dev/null || break
while read -r -u 3 line
echo "$line"
done
exec 3>&-
done
Полезные ссылки:
https://wiki.bash-hackers.org/howto/redirection_tutorial