如何使用“find -exec”在找到的文件的目录(不是当前目录)中执行命令?

如何使用“find -exec”在找到的文件的目录(不是当前目录)中执行命令?

假设我所在的目录有一些子目录 、dir1dir2dir3。每个目录都有一个文件foo,我想在foo每个目录中的每个目录上执行相同的命令,并从该目录内部执行该命令。

如果我“手工”完成它,它看起来会像这样:

cd dir1
(execute on foo)
cd ../dir2
(execute on foo)
cd ../dir3
(execute on foo)

必须执行该命令从每个目录foo是一个批处理调度脚本(对于 HTCondor,如果您想知道)并且必须从每个子目录执行,以便调度脚本启动的运行的输出将最终出现在每个子目录中。

问题“查找文件并在文件目录中执行命令据我所知,没有回答我的问题。该问题的第一个答案更多的是一种解决方法,在我的情况下不起作用,第二个答案没有足够的解释让我知道如何使用它。

答案1

假设您find支持它,请使用该-execdir选项而不是-exec

find . -type f -name 'foo' -execdir pwd \;

这将依次更改为每个目录,如果foo那里存在文件,它将运行pwd

注意:您无法重新利用此解决方案在名为 的目录中运行命令foo。为此,您需要恢复到一种简单的-exec sh -c '...'方法,迭代提供给每个参数shcd依次使用每个参数。此示例pwd在每个目录中运行:

find . -type d -name 'foodir' -exec sh -c '
    for d in "$@"; do ( cd "$d" || exit; pwd ); done
    ' _ {} +

答案2

假设您确切知道在哪些目录中运行命令,

for dir in dir1 dir2 dir3; do
    ( cd "$dir" && somecommand foo )
done

或者,循环遍历foo文件,

for foo in ./*/foo; do
  ( cd "${foo%/foo}" && somecommand foo )
done

子 shell 可以防止cd影响脚本其余部分的工作目录。参数${foo%/foo}替换扩展到文件所在目录的名称(通过从 的值中foo删除 Knows 后缀字符串)。/foo$foo

使用标准来做同样的事情(在文件可能位于目录结构深处的find情况下):foo

find . -type f -name foo -exec sh -c '
    for foo do
        ( cd "${foo%/foo}" && somecommand foo )
    done' sh {} +

用言语来说:查找当前目录或以下任意位置的所有名为“foo”的常规文件,并将其路径名批量提供给此 shell 脚本。 shell 脚本应迭代给定的路径名​​,将目录更改为每个路径名的目录部分,如果成功,则执行somecommand foo.

有关使用非标准的解决方案-execdir,请参阅罗艾玛的回答。他的回答适合我的例子:

find . -type f -name foo -execdir somecommand foo ';'

答案3

也许不漂亮,但我认为它可以完成工作

find . -name 'foo' -type f -exec sh -c 'cd $(echo {}|cut -d"/" -f2) && ./foo' \;

答案4

在 Win 10 上的 WSL 和 GitBash 中进行了测试。

以前的解决方案中的 execdir 将不起作用,因为该命令将使用当前目录作为参数执行,而不是进入其中。
您只需 cd 进入该目录并执行您想要的操作即可。
例如,这是我在开始工作之前用来更新所有存储库的方法

find $HOME/workspace/ -maxdepth 1 -type d -name "repo*" -exec bash -c "cd \"{}\" && echo ____Pulling from \"{}\" && git pull" \;

编辑:
如果您不在个人电脑/笔记本电脑上并且需要更强大的代码,您应该编写一个不可使用外部命令注入的脚本,例如:

#!/bin/sh
find "$HOME/workspace/" -maxdepth 1 -type d -exec sh -c '
  for dir in "$@"
  do
    echo "____Pulling from $dir"
    cd "$dir"
    git pull
  done' sh {} +

相关内容