如何通过 find+exec 设置变量?

如何通过 find+exec 设置变量?

我正在尝试编写一个 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亮点,并且grepping 其输出是轻而易举的事。

您可以轻松地做到这一点:

ls -1aqRsp "$LOCATION" 2>&1 | grep -qv "^ *[^0]\|/"
FLAG=$(($?==0))

这只会设置FLAG1如果文件 - .dot包含的文件 - 存在于$LOCATION或其子目录之一中,大小为零。否则,$FLAG0.

答案2

find当你这样做时,正在分叉一个新的子进程-exec。任何子进程都不能改变其父进程的环境。

您可以考虑收集有问题的文件名find,然后在第二遍中生成您想要的电子邮件:

find . -type f -size 0 -print >> /var/tmp/find.sz0
...

答案3

这是在调用的export FLAG=1实例中执行的。进程的环境永远不会复制回其父进程。仅设置环境变量并退出的 shell 进程不会完成任何持久任务。shfind

如果你想测试是否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
}

相关内容