我正在尝试编写一个 bash 脚本,在其中尝试查找某个位置是否有空文件,如果找到则发送电子邮件。我首先想到将“查找”和“邮件”组合在一起,但是如果该位置有多个空文件,它会发送多封邮件,这是我不想要的,所以我想到在查找并设置它之前将标志变量设置为零到 1 里面查找是否找到空文件。这是我尝试过的:
FLAG=0
find $LOCATION -size 0 -type f -exec sh -c 'export FLAG=1' \;
echo $FLAG
但问题是,即使该位置有空文件,标志值也不会更改为 1。我做错了什么?
答案1
set -- "${LOCATION}/"*
while [ -s "$1" ] ; do shift ; done
[ -e "$1" ] && FLAG=1
shell 内置命令[ test ]
可以与ize 运算符一起使用-s
。从man test
:
-s FILE
FILE
存在且大小大于零
但这对于递归搜索来说并不容易。
find
会,但对于像设置布尔值这样简单的事情,它确实不太适合这种情况。ls
可以同样快速地进行递归搜索,可以轻松配置为提供文件大小作为列表的第一个字段,并保证每行仅列出一个条目。如果您想根据递归文件列表是否包含 0 大小的文件来设置 shell 变量的布尔值,那么这ls
是您最好的选择 -find
只会使事情变得复杂。您感兴趣的是文件属性,而不是文件位置。这就是它的ls
亮点,并且grep
ping 其输出是轻而易举的事。
您可以轻松地做到这一点:
ls -1aqRsp "$LOCATION" 2>&1 | grep -qv "^ *[^0]\|/"
FLAG=$(($?==0))
这只会设置FLAG
为1
如果文件 -隐 .dot
包含的文件 - 存在于$LOCATION
或其子目录之一中,大小为零。否则,$FLAG
是0
.
答案2
find
当你这样做时,正在分叉一个新的子进程-exec
。任何子进程都不能改变其父进程的环境。
您可以考虑收集有问题的文件名find
,然后在第二遍中生成您想要的电子邮件:
find . -type f -size 0 -print >> /var/tmp/find.sz0
...
答案3
这是在调用的export FLAG=1
实例中执行的。进程的环境永远不会复制回其父进程。仅设置环境变量并退出的 shell 进程不会完成任何持久任务。sh
find
如果你想测试是否find
匹配任何文件,你可以简单地测试它的输出是否为空。为了避免可能生成一长串文件,但在证明它非空后才将其丢弃,您可以find
在第一时间截断输出。 GNU find 有一个-quit
谓词,您也可以轻松地告诉它显示单个字符。在下面的代码片段中,FLAG
如果没有匹配,则结果为空:
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
以下变量设置FLAG
为 0 或 1:
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
[ -n "$FLAG" ] || FLAG=0
如果您想保持便携性,您可以进行find
打印匹配并仅保留第一行;find
会死于信号管道head
关闭管道的一侧后。
if [ -n "$(find "$LOCATION" -size 0 -type f -print | head -n 1 \;)" ]; then
FLAG=1
else
FLAG=0
fi
如果这只是一个简化的示例,并且您需要设置更复杂的变量,则有几种可能的方法。我只提几个常见的。
Zsh 有很多find
内置的用例,这要归功于全局限定符。以下代码片段设置files
为 匹配的文件列表find $LOCATION -size 0 -type f
:
files=(**/*(.DL0))
set -o globstar
如果分别使用和启用,Ksh 和 bash 也具有递归 glob shopt -s globstar
。但请注意,bash 到 4.2 版本时,**
会递归到目录的符号链接,这通常是不可取的。您可以在 shell 中进行进一步的处理。
FIGNORE= # include dot files in wildcard matches (the ksh way)
GLOBIGNORE=.:.. # include dot files in wildcard matches (the bash way)
for x in **/*; do
done
如果匹配的文件名都不包含换行符,您可以将 的输出解析为find
换行符分隔列表。
find "$LOCATION" -size 0 -type f ! -name '*
*' -print | {
while read -r empty_file; do
…
done
}