我编写了以下脚本,以交互方式和递归方式删除孤立的备份文件,即删除每个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
这个脚本会随机做出一些非常奇怪的事情。有时它表现正常,有时它会忽略传递给 sh 的 -x 选项,有时它会执行注释代码,有时测试会给出错误的结果。
问题似乎与此处脚本有关,因为通过将 find 的输出重定向到临时文件,所有问题似乎都消失了。但为什么,错误在哪里?
解决方案(谢谢丹尼斯·威廉森): 转义字符。参数扩展中的~
未转义字符以某种方式创建了所有不可预测的行为。~
${file%~}
一个更可读、更确定的解决方案,通过一些删减,可能是(感谢米克尔):
#!/bin/sh
IFS='
'
for backup in $(find . -type f -name "*~"); do
if [ ! -e "${backup%\~}" ]; then
rm -i "$backup"
fi
done
如果你是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
或者,也可以采用更复杂的方式丹尼斯·威廉森。
答案1
您可能需要转义括号扩展中的波浪号,否则它将扩展到您的主目录。
为什么不将 放入循环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
到循环末尾,这样就不会创建子 shell。
#!/bin/bash
...
# use an alternate file descriptor so read and rm -i get along
while read -u 3 -r ...
rm -i ...
...
done 3< <(find ...)
...
答案2
“有时”是什么意思?有时在同一台机器上?还是在不同系统上有不同的行为?
“执行注释代码”是什么意思?您的示例没有任何注释。
一些想法:
- 尝试
set -x
代替/bin/sh -x
set -e
比使用更好set -o errexit
- 如果你使用 bash,则调用
/bin/bash
,而不是/bin/sh
,这可能是其他东西 - echo 是不必要的,只需
=
与文字换行符一起使用即可 - 如果你必须使用
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