Bash脚本:一种更好的方法来删除带有通配符的文件和目录列表
我想传递一个文件列表,包括通配符文件和我想要删除的目录,但检查它们是否在删除之前先存在。如果它们被删除,则通知该目录已被删除,而不是目录中的每个单独文件。即如果我删除/root/*.tst只是说,“我删除* .tst”。Bash脚本:一种更好的方法来删除带有通配符的文件和目录列表
#!/bin/bash
touch /boot/{1,2,3,4,5,6,7,8,9,10}.tst
touch /root/{1,2,3,4,5,6,7,8,9,10}.tst
mkdir /root/tmpdir
#setup files and directories we want to delete
file=/boot/*.tst /root/*.tst /root/tmpdir
for i in $file; do
if [[ -d "$file" || -f "$file" ]]; then #do I exist
rm -fr $i
echo removed $i #should tell me that I removed /boot/*.tst or /root/*.tst or /root/tmpdir
else
echo $i does not exist # should tell me if /boot/*.tst or /root/*.tst or /root/tmpdir DNE
fi
done
我似乎无法使单或双引号或转义的任何组合*使上述做我想做的事。
非常感谢每个人的职位,包括约翰库格尔曼。
这是我最终用来提供两种删除类型的代码。第一个是删除所有内容更有力。第二个保存的目录结构只是删除文件。如上所述,请注意,文件名中的空格不是由此方法处理的。
rmfunc() {
local glob
for glob in "[email protected]"; do
local matched=false
local checked=true
for path in $glob; do
$checked && echo -e "\nAttempting to clean $glob" && checked=false
[[ -e $path ]] && rm -fr "$path" && matched=true
done
$matched && echo -e "\n\e[1;33m[\e[0m\e[1;32mPASS\e[1;33m]\e[0m Cleaned $glob" || echo -e "\n\e[1;33m[\e[0m\e[1;31mERROR\e[1;33m]\e[0m Can't find $glob (non fatal)."
done
}
# Type 2 removal
xargfunc() {
local glob
for glob in "[email protected]"; do
local matched=false
local checked=true
for path in $glob; do
$checked && echo -e "\nAttempting to clean $glob" && checked=false
[[ -n $(find $path -type f) ]] && find $path -type f | xargs rm -f && matched=true
done
$matched && echo -e "\n\e[1;33m[\e[0m\e[1;32mPASS\e[1;33m]\e[0m Cleaned $glob" || echo -e "\n\e[1;33m[\e[0m\e[1;31mERROR\e[1;33m]\e[0m Can't find $glob (non fatal)."
fi
}
在解释为什么你的代码没有,这里是你应该使用什么:
for i in /boot/*.txt /root/*.txt /root/tmpdir; do
# i will be a single file if a pattern expanded, or the literal
# pattern if it does not. You can check using this line:
[[ -f $i ]] || continue
# or use shopt -s nullglob before the loop to cause non-matching
# patterns to be silently ignored
rm -fr "$i"
echo removed $i
done
看来,希望i
被设置到每个三种模式,这是一个有点棘手,也许应该应避免,因为大多数您使用的操作符都需要单个文件或目录名称,而不是匹配多个名称的模式。
的尝试告诉你
file=/boot/*.tst /root/*.tst /root/tmpdir
将扩大/root/*.tst
,并尝试在扩张作为命令名称,工作的环境变量file
有字面值/boot/*.tst
执行使用的第一个名字。包括字符串中的所有模式,你需要逃避它们之间的空间,有两种
file=/boot/*.tst\ /root/*.tst\ /root/tmpdir
或多种天然
file="/boot/*.tst /root/*.tst /root/tmpdir"
无论哪种方式,模式尚未展开;文字*
存储在值file
中。然后,将扩大其使用
for i in $file # no quotes!
和$file
膨胀到其文本值后,存储的模式将扩展到该组匹配文件名。但是,此循环仅适用于不包含空格的文件名;名为foo bar
的单个文件将被视为分配给i
的两个单独的值,即foo
和bar
。对付这样的文件名中bash
正确的方法是使用一个数组:
files=(/boot/*.tst /root/*.tst /root/tmpdir)
# Quote are necessary this time to protect space-containing filenames
# Unlike regular parameter assignment, the patterns were expanded to the matching
# set of file names first, then the resulting list of files was assigned to the array,
# one file name per element.
for i in "${files[@]}"
您可以通过
printf -v file "%s " /boot/*.tst /root/*.tst /root/tmpdir
更换
file=/boot/*.tst /root/*.tst /root/tmpdir
壳自动扩展水珠。如果您希望能够在错误消息中打印字面大小,那么您需要引用它们。
rmglob() {
local glob
for glob in "[email protected]"; do
local matched=false
for path in $glob; do
[[ -e $path ]] && rm -rf "$path" && matched=true
done
$matched && echo "removed $glob" || echo "$glob does not exist" >&2
done
}
rmglob '/boot/*.tst' '/root/*.tst' '/root/tmpdir'
请注意仔细使用引用。引用了deleteGlobs
的参数。函数内部的$glob
变量是而不是引用(for path in $glob
),它触发了当时的外壳扩展。
这只适用于不包含空格的模式(例如,'rmglob'/ root/tmp dir''不起作用)。 – chepner 2014-09-12 23:59:50
这几乎完全是我正在寻找通知删除的东西。你将如何修改上述内容以确保捕获带有空格的文件? – vlord 2014-09-15 15:07:26