我需要递归遍历文件夹的所有子目录。在子目录中,如果有一个扩展名为“.xyz”的文件,那么我需要在该文件夹中运行一次特定命令。
这是我到目前为止所拥有的
recursive() {
for d in *; do
if [ -d "$d" ]; then
(cd -- "$d" && recursive)
fi
dir=`pwd`
pattern="*.xyz"
file_count=$(find $dir -name $pattern | wc -l)
if [[ $file_count -gt 0 ]]; then
echo "Match found. Going to execute a command"
#execute command
fi
done
}
(cd /target; recursive)
但问题是,当存在匹配项时,“找到匹配项..”消息会在每个文件夹中显示多次。在解决这个问题的同时,有没有更简单的方法来做到这一点?
答案1
你正在重新发明find
。
尝试这样的事情(使用 GNUfindutils
和 GNU sort
):
find /target -iname '*.xyz' -printf '%h\000' | sort -z -u |
xargs -0 -r -I {} sh -c "cd {} ; yourcommandhere"
打印找到“*.xyz”文件的-printf
目录名称 ( ),并以 NUL 字节 ( ) 作为分隔符。 用于消除重复项,然后用于进入每个目录并运行。%h
\000
sort
xargs
cd
yourcommandhere
您还可以编写一个脚本来使用 xargs 运行。例如
find /target -iname '*.xyz' -printf '%h\000' | sort -z -u |
xargs -0 -r /path/to/myscript.sh
简单的 myscript.sh 示例:
#!/bin/sh
for d in "$@" ; do
cd "$d"
echo "Match found in $d. Going to execute command"
# execute command
done
如果有许多匹配的目录,第二个版本将明显更快 - 它只需分叉一次 shell(然后迭代每个参数),而不是为每个目录分叉一次 shell。
顺便说一句,这里实际上既不printf
需要sort
也不xargs
需要......但它们确实使阅读和理解正在发生的事情变得更容易。同样重要的是,通过尽早消除重复项(使用 printf 和 sort),它的运行速度比仅使用 bash 快得多,并且消除了在任何给定目录中多次执行命令的风险(相当小)。
这是执行相同操作的另一种方法,无需排序或 xargs:
find /target -iname '*.xyz' -exec bash -c \
'typeset -A seen
for f in "$@"; do
d="$(dirname "$f")";
if [[ ! -v $seen[$d] ]]; then
echo "Match found in $d. Going to execute command"
# Execute command
seen["$d"]=1
fi
done' {} +
这使用 bash ( ) 中的关联数组$seen[]
来跟踪已查看和处理的目录。请注意,如果有数千个匹配*.xml
文件(足以超过最大命令行长度,以便 bash 脚本分叉多次),那么您的命令可能在任何给定目录中运行多次。
由 find 选项执行的脚本-exec
可以是独立脚本,与上面的 xargs 版本一样。
顺便说一句,这里的任何变体都可以轻松执行 awk 或 perl 或任何脚本,而不是 sh 或 bash 脚本。
答案2
find
有一个内置标志来打印字符串,这在这里非常有用:
find -iname "*.xyz" -printf "%h\n"
打印包含与您的模式匹配的文件的所有目录的名称(%h
is justfind
的神奇语法扩展到文件目录,\n
当然是换行符)。
因此,这就是你想要的:
COMMAND='echo'
find `pwd` -iname "*.pdf" -printf "%h\n" | sort -u | while read i; do
cd "$i" && pwd && $COMMAND
done
这里发生了一些事情。为了只执行一次命令,我们只需使用标志将其通过管道传输sort
,-u
这会删除所有重复的条目。然后我们用 循环遍历所有内容while
。另请注意,我使用了find `pwd`
,这是一个很好的技巧,可以find
输出绝对路径,而不是相对路径,这使我们可以使用cd
而不必担心任何相对路径。
编辑:执行此脚本时请小心您的目录名称,因为目录名称包含换行符 ( \n
) 甚至\
可能会破坏脚本(也许还有其他不常见的字符,但我还没有测试更多)。解决这个问题很困难,我不知道该怎么做,所以我只能建议不要使用这样的目录。