有没有办法在将 `find` 的结果传递给 `-exec` 之前 "map"?
Is there a way to "map" the results of `find` before passing them to `-exec`?
在下面的命令中,我得到了一个文件路径列表,我想在对每个文件路径执行 运行 多个 -exec
命令之前获取每个文件路径的基本名称。
find /usr/local/lib/systemd -type f \
-exec bash -c "basename {} | xargs echo 'stopping'" \; \
-exec bash -c "basename {} | xargs systemctl stop" \; \
-exec bash -c "basename {} | xargs systemctl disable" \;
有没有办法不必每次都调用 basename {}
来执行此操作?
回答
find /usr/local/lib/systemd -type f -exec basename {} \; | xargs -L1 -I {} bash -c "echo 'stopping' {}; systemctl stop {}; systemctl disable {}"
说明
首先看一下find
的输出
find /usr/local/lib/systemd -type f
/usr/local/lib/systemd/file1.service
/usr/local/lib/systemd/file2.service
/usr/local/lib/systemd/file3.service
如果您使用 -exec
,那本质上就是一张地图。例如
find /usr/local/lib/systemd -type f -exec basename {} \;
file1.service
file2.service
file3.service
然后您可以将其通过管道传输到 xargs
并使用 -L
选项一次将 N 个参数传递给命令。在这种情况下,-L1
将对输入中的每个 1
行重复 xargs
命令。查看命令仅打印“test”和文件名的示例:
find /usr/local/lib/systemd -type f -exec basename {} \; | xargs -L1 echo "test"
test file1.service
test file2.service
test file3.service
您可以使用 在单个 bash 命令中多次替换参数,如下所示:
find /usr/local/lib/systemd -type f -exec basename {} \; | xargs -L1 -I {} echo hello {} world {}
hello file1.service world file1.service
hello file2.service world file2.service
hello file3.service world file3.service
最后,您可以使用bash -c "..."
到运行多个命令作为一个命令。将其用作 xargs
命令,如下所示:
find /usr/local/lib/systemd -type f -exec basename {} \; | xargs -L1 -I {} bash -c "echo 'stopping' {}; systemctl stop {}; systemctl disable {}"
systemctl
采用 glob 模式,所以你可以这样做:
echo stopping everything
systemctl stop \*
systemctl disable \*
引用模式很重要,这样它就不会被 shell 扩展。
另请注意 man systemctl
> 参数语法:“shell-style glob 将与当前内存中所有单位的主要名称相匹配”。因此在某些情况下它可能不等同于您的查找方法。
为了更笼统地回答您的问题,更好地控制 find
生成的文件列表的一个好方法是将其传递给 shell 循环:
find /usr/local/lib/systemd -type f -exec bash -c '
for i do
i=$(basename "$i")
echo "$i"...
systemctl stop "$i"
systemctl disable "$i"
done' _ {} +
此外,在这种情况下,因为 systemctl
可以采用多个单位参数,所以您可以这样做:
-exec bash -c '
systemctl stop "${@##*/}"
systemctl disable "${@##*/}"' _ {} +
这会调用 systemctl
两次*,而不是多次调用,这样效率更高。 ${@##*/}
去除每个位置参数的前缀直到最后一个斜杠,与 basename
.
相同
* 如果文件列表很大,find 可能会多次调用 -exec
命令 (bash
)。
在下面的命令中,我得到了一个文件路径列表,我想在对每个文件路径执行 运行 多个 -exec
命令之前获取每个文件路径的基本名称。
find /usr/local/lib/systemd -type f \
-exec bash -c "basename {} | xargs echo 'stopping'" \; \
-exec bash -c "basename {} | xargs systemctl stop" \; \
-exec bash -c "basename {} | xargs systemctl disable" \;
有没有办法不必每次都调用 basename {}
来执行此操作?
回答
find /usr/local/lib/systemd -type f -exec basename {} \; | xargs -L1 -I {} bash -c "echo 'stopping' {}; systemctl stop {}; systemctl disable {}"
说明
首先看一下find
find /usr/local/lib/systemd -type f
/usr/local/lib/systemd/file1.service
/usr/local/lib/systemd/file2.service
/usr/local/lib/systemd/file3.service
如果您使用 -exec
,那本质上就是一张地图。例如
find /usr/local/lib/systemd -type f -exec basename {} \;
file1.service
file2.service
file3.service
然后您可以将其通过管道传输到 xargs
并使用 -L
选项一次将 N 个参数传递给命令。在这种情况下,-L1
将对输入中的每个 1
行重复 xargs
命令。查看命令仅打印“test”和文件名的示例:
find /usr/local/lib/systemd -type f -exec basename {} \; | xargs -L1 echo "test"
test file1.service
test file2.service
test file3.service
您可以使用
find /usr/local/lib/systemd -type f -exec basename {} \; | xargs -L1 -I {} echo hello {} world {}
hello file1.service world file1.service
hello file2.service world file2.service
hello file3.service world file3.service
最后,您可以使用bash -c "..."
到运行多个命令作为一个命令。将其用作 xargs
命令,如下所示:
find /usr/local/lib/systemd -type f -exec basename {} \; | xargs -L1 -I {} bash -c "echo 'stopping' {}; systemctl stop {}; systemctl disable {}"
systemctl
采用 glob 模式,所以你可以这样做:
echo stopping everything
systemctl stop \*
systemctl disable \*
引用模式很重要,这样它就不会被 shell 扩展。
另请注意 man systemctl
> 参数语法:“shell-style glob 将与当前内存中所有单位的主要名称相匹配”。因此在某些情况下它可能不等同于您的查找方法。
为了更笼统地回答您的问题,更好地控制 find
生成的文件列表的一个好方法是将其传递给 shell 循环:
find /usr/local/lib/systemd -type f -exec bash -c '
for i do
i=$(basename "$i")
echo "$i"...
systemctl stop "$i"
systemctl disable "$i"
done' _ {} +
此外,在这种情况下,因为 systemctl
可以采用多个单位参数,所以您可以这样做:
-exec bash -c '
systemctl stop "${@##*/}"
systemctl disable "${@##*/}"' _ {} +
这会调用 systemctl
两次*,而不是多次调用,这样效率更高。 ${@##*/}
去除每个位置参数的前缀直到最后一个斜杠,与 basename
.
* 如果文件列表很大,find 可能会多次调用 -exec
命令 (bash
)。