我正在制作一个高级删除脚本。这个想法是用户输入一个 grep 正则表达式来查找需要删除的内容,然后脚本对所有内容执行 rm 操作。基本上不需要每次都直接在命令行中编写所有代码。
这是我到目前为止的脚本:
#!/bin/bash
# Script to delete files passed to it
if [ $# -ne 1 ]; then
echo "Error! Script needs to be run with a single argument that is the regex for the files to delete"
exit 1
fi
IFS=$'\n'
files=$(ls -a | grep $1 | awk '{print "\"" $0 "\"" }')
## TODO ensure directory support
echo "This script will delete the following files:"
for f in $files; do
echo " $f"
done
valid=false
while ! $valid ; do
read -p "Do you want to proceed? (y/n): "
case $REPLY in
y)
valid=true
echo "Deleting, please wait"
echo $files
rm ${files}
;;
n)
valid=true
;;
*)
echo "Invalid input, please try again"
;;
esac
done
exit 0
我的问题是当我实际执行“rm”操作时。我不断收到错误消息,提示“没有这样的文件或目录”。
这是我正在使用的目录:
drwxr-xr-x 6 user staff 204 May 9 11:39 .
drwx------+ 51 user staff 1734 May 9 09:38 ..
-rw-r--r-- 1 user staff 10 May 9 11:39 temp two.txt
-rw-r--r-- 1 user staff 6 May 9 11:38 temp1.txt
-rw-r--r-- 1 user staff 6 May 9 11:38 temp2.txt
-rw-r--r-- 1 user staff 10 May 9 11:38 temp3.txt
我这样调用脚本:
easydelete.sh '^tem'
这是输出:
This script will delete the following files:
"temp two.txt"
"temp1.txt"
"temp2.txt"
"temp3.txt"
Do you want to proceed? (y/n): y
Deleting, please wait
"temp two.txt" "temp1.txt" "temp2.txt" "temp3.txt"
rm: "temp two.txt": No such file or directory
rm: "temp1.txt": No such file or directory
rm: "temp2.txt": No such file or directory
rm: "temp3.txt": No such file or directory
如果我尝试直接删除其中一个文件,效果很好。如果我什至在调用“rm”之前传递了打印出来的整个字符串,它就可以正常工作。但是当我用数组执行此操作时,它失败了。
我做错了什么?
考虑一下:
# put all filenames containing $1 as literal text in an array
#files=( *"$1"* )
# ...or, use a grep with GNU extensions to filter contents into an array:
# this passes filenames around with NUL delimiters for safety
#files=( )
#while IFS= read -r -d '' f; do
# files+=( "$f" )
#done < <(printf '%s\0' * | egrep --null --null-data -e "$1")
# ...or, evaluate all files against $1, as regex, and add them to the array if they match:
files=( )
for f in *; do
[[ $f =~ $1 ]] && files+=( "$f" )
done
# check that the first entry in that array actually exists
[[ -e $files || -L $files ]] || {
echo "No files containing $1 found; exiting" >&2
exit 1
}
# warn the user
echo "This script will delete the following files:" >&2
printf ' %q\n' "${files[@]}" >&2
# prompt the user
valid=0
while (( ! valid )); do
read -p "Do you want to proceed? (y/n): "
case $REPLY in
y) valid=1; echo "Deleting; please wait" >&2; rm -f "${files[@]}" ;;
n) valid=1 ;;
esac
done
我将详细介绍以下内容:
files
必须显式创建为数组才能真正 be 一个数组 - 否则,它只是一个包含一堆文件的字符串。
这是一个数组:
files=( "first file" "second file" )
这不是一个数组(事实上,可能是一个单个文件名):
files='"first file" "second file"'
正确的 bash 数组可以使用
"${arrayname[@]}"
展开以获取所有内容,或者使用 "$arrayname"
来仅获取第一个条目。
[[ -e $files || -L $files ]]
...因此检查数组中 first 条目是否存在(无论是作为文件还是符号链接)——这足以判断 glob 表达式是否确实扩展,或者是否不匹配任何内容。
布尔值比包含
true
或 false
的字符串更好地用数值表示:如果 if $valid
的内容可以设置为用户控制的值,则运行 valid
有可能执行任意活动,而if (( valid ))
——检查$valid
是否为正数值(true)或其他值(false)——在其他地方存在错误时产生副作用的空间要小得多。无需循环遍历数组条目即可将它们打印在列表中:只要参数(来自数组扩展)多于其格式字符串所需的参数,
printf "$format_string" "${array[@]}"
就会额外扩展格式字符串。此外,在格式字符串中使用 %q
将引用不可打印的值、空格、换行符等。采用人类读者和 shell 都可以使用的格式——否则使用 touch $'evil\n - hiding'
创建的文件将显示为两个列表条目,而实际上它只有一个。