为什么 xargs on find 不能与使用 pushd 和 popd 的一系列命令一起遍历目录子树?

为什么 xargs on find 不能与使用 pushd 和 popd 的一系列命令一起遍历目录子树?

在 Linux 上CentOS4 机器,我正在尝试创建一个简单的 Bash 命令行来遍历任意当前目录下的目录结构,并在每个子目录中触摸一个文件,列出目录内容但将它们通过管道传输到/dev/null,并删除被触摸的文件。

这个脚本的隐晦之处在于触动底层NFS客户端/服务器系统,以确保每个目录的内容反映在不同机器上所做的更改,否则可能需要一些时间才能传播。我发现这种解决方法可以避免延迟。忽略我这样做的理由,为什么我提议的 Bash 脚本不起作用?

[CentosMachine] find . -type d -print0 | xargs -0 -I {} pushd {}; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd
xargs: pushd: No such file or directory
bash: popd: directory stack empty

find命令目前正在返回:

.
./dir
./emptyDir
./dirOfDir
./dirOfDir/ofDir
./dirOfDir/ofDir/Dir(empty)

起初我以为可能是目录名称中的(和可能是问题所在,但将该目录重命名为并没有改变症状。我还尝试查看)./dirOfDir/ofDir/Dir_empty_斯特拉斯输出,但我没有看到任何有帮助的东西,但我确实看到正在处理的目录。

以下是输出末尾的片段strace,其中目录重命名为使用下划线而不是括号:

[...]
chdir("ofDir")                          = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
lstat64("Dir_empty_", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
open("Dir_empty_", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 4
fstat64(4, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
fcntl64(4, F_SETFD, FD_CLOEXEC)         = 0
getdents64(4, /* 2 entries */, 32768)   = 48
getdents64(4, /* 0 entries */, 32768)   = 0
close(4)                                = 0
chdir("Dir_empty_")                     = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
chdir("..")                             = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
chdir("..")                             = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
chdir("..")                             = 0
lstat64(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
fchdir(3)                               = 0
write(1, ".\0./dir\0./emptyDir\0./dirOfDir\0./"..., 75) = 75
exit_group(0)                      = ?

答案1

xargs是一个非常有用的工具

  1. 使用从动态源获取的参数执行命令,以及
  2. 通过构建带有多个参数的长命令行来最大限度地减少命令调用的次数。

当您不执行 #2 时(即,像您正在做的那样,每个参数执行一个命令), xargs就不那么有价值了;还有其他方法可以执行命令。特别是,如果参数的来源是find,您可以使用以下-exec选项:

find . -type d -exec bash -c 'pushd "{}" &> /dev/null; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd &> /dev/null'

但是这个答案和你的一样,会为每个目录调用一个 shell 进程。每个进程都有自己的执行环境;更改子进程中的工作目录对父进程没有影响。所以你不需要 and pushdpopd你也不需要指定bash;普通的sh就可以了。而且,如果你没有设置修改时间,你就不需要touch;重定向的空命令将创建一个文件。所以我们可以将上述内容简化为:

find . -type d -exec sh -c 'cd "{}"; > xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ'

对于您的用例,这可能并不重要,但在其他情况下,您可能只想在cd成功时执行最后三个步骤:

find . -type d -exec sh -c 'cd "{}" && { > xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ;}'

请注意, 之后需要有空格,{之前需要有分号}

答案2

我找到了答案Stack Overflow 问题. 将多个命令放入如下形式:

bash -c 'command1; command2; ...'

此处应用可得出:

find . -type d -print0 | xargs -0 -I {} bash -c 'pushd "{}"; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd'

注意在 周围添加双引号,pushd "{}"以便目录()正常工作。如果没有,则会收到错误:

bash: -c: line 0: syntax error near unexpected token `('
bash: -c: line 0: `pushd ./dirOfDir/ofDir/Dir(empty) &> /dev/null; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd &> /dev/null'

然而,pushedpopd也需要抑制以避免输出:

find . -type d -print0 | xargs -0 -I {} bash -c 'pushd "{}" &> /dev/null; touch xYzZy.fixZ; ls &> /dev/null; rm -f xYzZy.fixZ; popd &> /dev/null'

相关内容