Я записал следующий сценарий в в интерактивном режиме, и рекурсивно удалите файлы резервных копий висячей строки, т.е. удалите каждого file.txt~
это не имеет соответствия file.txt
.
#!/bin/sh -x
set -o errexit
unalias -a
backups=$(find . -name "*~")
orphans=""
while read -r file
do
[ ! -e "${file%~}" ] && orphans=$(echo "$file\n$orphans");
done << EOF
$backups
EOF
if [ -z "$orphans" ]; then
echo "No orphans."
else
echo "orphans:\n$orphans"
echo "$orphans" | xargs --interactive -d '\n' rm
fi
Этот сценарий делает очень странные вещи случайным способом. Иногда это ведет себя правильно, иногда это игнорирует-x опции, переданные sh, иногда это выполняет прокомментированный код, иногда тесты дают просто неправильные результаты.
Проблема, кажется, связана со здесь сценарий, с тех пор путем перенаправления вывода находки во временный файл, все проблемы, кажется, исчезают. Но почему, где ошибка?
Решение (благодаря Dennis Williamson): выйдите ~
символ. Незавершенное ~
в расширении параметра ${file%~}
создавал так или иначе все непредсказуемое поведение.
Более читаемое и детерминированное решение, с небольшим количеством отключенного жира, могло быть (благодаря предложениям Mikel):
#!/bin/sh
IFS='
'
for backup in $(find . -type f -name "*~"); do
if [ ! -e "${backup%\~}" ]; then
rm -i "$backup"
fi
done
Если Вы - a while read
вентилятор цикла, вещи становятся менее изящными потому что интерактивная команда rm -i
не может использоваться (это будет конфликтовать с read
команда). Так или иначе решение могло быть:
#!/bin/sh
orphans=""
while read -r backup; do
if [ ! -e "${backup%\~}" ]; then
orphans=$(echo "$backup\n$orphans");
fi
done << EOF
$(find . -type f -name "*~")
EOF
if [ ! -z "$orphans" ]; then
echo "$orphans" | xargs --interactive -d '\n' rm
fi
Или, более сложный путь также предлагается Dennis Williamson.
Вероятно, необходимо выйти из тильды в расширении фигурной скобки, иначе это будет расширено до корневого каталога.
Почему Вы не передаете по каналу find
в Ваш while
цикл вместо того, чтобы создать переменные для содержания их? В Вашем цикле просто сделайте rm -i "$file"
.
#!/bin/sh -x
set -o errexit
unalias -a
exec 3<&0 # open a duplicate of stdin
flag=false
find . -name "*~" | while IFS=$'\n' read -r file
do
if [ ! -e "${file%\~}" ]
then
orphans="$file"$'\n'"$orphans"
# use an alternate file descriptor so read and rm -i get along
rm -i "$file" <&3
flag=true
fi
done
exec 3<&- # close the file descriptor
if ! $flag
then
echo "No orphans."
else
echo "orphans:\n$orphans"
fi
Если Вы хотите использовать Bash, необходимо переместиться find
в конец цикла, таким образом, не создается подоболочка.
#!/bin/bash
...
# use an alternate file descriptor so read and rm -i get along
while read -u 3 -r ...
rm -i ...
...
done 3< <(find ...)
...
Что Вы имеете в виду "иногда"? Иногда на том же поле? Или другое поведение в различных системах?
То, что Вы имеете в виду, "выполняет прокомментированный код"? Ваш пример не имеет никаких комментариев.
Некоторые мысли:
set -x
вместо /bin/sh -x
set -e
чем set -o errexit
/bin/bash
, нет /bin/sh
, который мог быть чем-то еще=
с литеральной новой строкойecho
, необходимо использовать echo -e
или удостоверьтесь xpg_echo
установленIFS
сначалаБолее простая версия:
#!/bin/bash
set -x
set -e
IFS=$'\n'
orphans=false
for backup in $(find . -type f -name "*~"); do
original=${backup%\~}
if [ ! -e "$original" ]; then
orphans=true
rm -i "$backup"
fi
done
if ! $orphans; then
echo "No orphans."
fi
Или если Вы хотите, чтобы это работало с помощью /bin/sh
:
#!/bin/sh
set -x
set -e
IFS='
'
orphans=false
for backup in $(find . -type f -name "*~"); do
original=${backup%\~}
if [ ! -e "$original" ]; then
orphans=true
rm -i "$backup"
fi
done
if ! $orphans; then
echo "No orphans."
fi