结合使用 xargs bash -c 创建复杂的命令

xargs 是你的 shell 命令的烟花。 从任何 shell 命令生成的任何输出都可以发送到 xargs 以在另一个命令行上进行处理。 立即了解如何利用 xargs 的强大功能!

xargs简介

我的同事 Dave McKay 写了一篇有趣的文章 How to Use the xargs Command in Linux,您可能想先阅读这篇文章,以全面了解 xargs 的详细介绍和探索。

本文将针对一个具体的问题:当你遇到传统或普通的基于管道的堆叠的局限性时怎么办(; 有界)堆叠 Linux 命令,甚至使用 xargs 似乎也无法立即提供答案?

这是在命令行上编写单行脚本和/或处理复杂数据或数据结构时的常见情况(如果不是经常的话)。 此处提供的信息基于研究和使用这种特定方法的多年经验。

管道和 xargs

任何学习 Bash 的人都会随着时间的推移提高他们的命令行脚本技能。 Bash 的伟大之处在于,在命令行上学到的任何技能都可以轻松地转换为 Bash 脚本(通常标有 .sh 后缀)。 语法一样好。

而且,随着技能的提高,新工程师通常会首先发现 Pipe 语言。 使用管道简单直接:前一个命令的输出“链接”到下一个命令的输入,可以将其想象成一根水管,将水从一个输出源输送到其他地方的输入源,通过 example 来自大坝的水通过管道输送到水轮机。

让我们看一个简单的 example:

echo 'a' | sed 's/a/b/'

在这里,我们简单地回显了 ‘a’,然后使用 sed 文本流编辑器对其进行了更改。 输出自然是“b”:sed 将(s 命令)“a”替换为“b”。

一段时间后,工程师会意识到管道的功能仍然有限,尤其是当人们想要以一种为下一个工具准备好的格式预处理数据时。 例如考虑这种情况:

在这里,我们开始了一个深深的梦想。 接下来我们使用 pidof 检索正在执行的挂起命令的 PID 并尝试使用 kill -9 (将 -9 视为终止进程的破坏性方式。) 失败。 然后,当我们启动后台进程时,我们尝试使用 shell 提供的 PID,但这以类似的方式失败了。

问题是 kill 不直接接受输入,无论是来自 ps 甚至一个简单的 echo. 为了解决这个问题,我们可以使用 xargs 来获取 ps 或者 echo 命令)并将它们作为输入提供给 kill,使它们成为 kill 命令的参数。 就好像我们执行了 kill -9 some_pid 直接地。 让我们看看这是如何工作的:

sleep 300 &
pidof sleep | xargs kill -9

这完美地工作并完成了我们打算做的事情:杀死睡眠进程。 代码中的一个小改动(即只需在命令前面添加 xargs),但 Bash 对开发工程师的用处却发生了很大的变化!

我们还可以使用 -I 选项(定义参数的替换字符串)到 kill 为了更清楚地说明我们如何将参数传递给 kill: i12b 在这里,我们定义 {} 作为我们的替换链。 也就是说,只要 xargs 看到 {}将取代它 {} 从最后一个命令接收到的任何输入。

尽管如此,即使这样也有其局限性。 如果我们想提供良好的内联打印调试信息并从语句中打印出来怎么办? 目前看来是不可能的。

是的,我们可以使用 sed 正则表达式,或插入一个子层($()) 某处,但所有这些似乎仍然有局限性,尤其是在创建复杂数据流时,无需使用新命令且无需使用中间临时文件。

如果我们可以一劳永逸地摆脱这些限制,100% 自由地创建我们喜欢的任何 Bash 命令行,只使用管道、xargs 和 Bash shell,没有临时中间文件,也没有启动新命令,那会怎样? ? 这是可能的。

如果有人向您展示它一点也不复杂,但第一次需要一些时间和讨论才能弄清楚。 我特别想表扬并感谢我之前 Linux 导师兼前同事安德鲁·达格利什 (Andrew Dalgleish)——不到 10 年前,我们一起想出了做到这一点的最佳方法。

欢迎来到 xargs Con bash -C

正如我们所见,即使管道与 xargs 结合使用,您仍然会遇到稍微高级工程师级别脚本的限制。 就拿我们之前的 example 并在不进一步处理结果的情况下注入一些调试信息。 通常这很难实现,但对于 xargs 结合 bash -c:

sleep 300 &
pidof sleep | xargs -I{} echo "echo 'The PID of your sleep process was: {}'; kill -9 {}; echo 'PID {} has now been terminated'" | xargs -I{} bash -c "{}"

xargs编译和使用的复杂脚本 bash -C

这里我们使用两个 xargs commands 第一个构建自定义命令行,使用管道中上一个命令的输出作为输入(是 pidof sleep) 并且第二个 xargs 命令运行生成的命令,根据输入定制(重要!)。

为什么要自定义每个输入? 原因是 xargs 默认情况下将逐行处理其输入(管道中上一个命令的输出),并将执行它被告知要为每一行输入执行的任何内容。

这里有很多力量。 这意味着您可以创建和编译任何自定义命令,然后运行它,无论输入数据的格式如何,而不必担心如何运行它。 您必须记住的唯一语法是:

some_command | xargs -I{} echo "echo '...{}...'; more_commands; more_commands_with_or_without{}" | xargs -I{} bash -c "{}"

请注意,嵌套 echo (第二个回声)只有在你想重新生成实际文本时才是真正必要的。 否则,如果第二个 echo 不在那里,第一个 echo 它会开始显示“The PID …”等。 和 bash -c 子 shell 将无法将其解析为命令(IOW,’The PID …’ 不是命令,不能这样执行,因此是辅助/嵌套回显)。

一旦你记起 bash -c-I{} 以及如何从另一个回声中回声(如果需要,可以选择使用转义序列),您会发现自己一遍又一遍地使用这种语法。

假设您必须为目录中的每个文件做三件事:1) 输出文件的内容,2) 将它移动到一个子目录,3) 删除它。 通常这需要一系列步骤和不同的暂存命令,如果它变得更复杂,您甚至可能需要临时文件。 但是使用起来很容易 bash -cxargs:

echo '1' > a
echo '2' > b
echo '3' > c
mkdir subdir
ls --color=never | grep -v subdir | xargs -I{} echo "cat {}; mv {} subdir; rm subdir/{}" | xargs -I{} bash -c "{}"

基于 xargs 和的全功能迷你脚本 bash -c 在单行命令上

首先,要快速注意使用 --color=never 为了 ls防止系统使用颜色编码目录列表输出出现问题(默认情况下启用 Ubuntu), 因为这通常会导致严重的解析问题,因为颜色代码被发送到终端并预先设置目录列表条目。

首先,我们创建三个文件 a、b 和 c,以及一个名为 subdir 的子目录。 我们从目录列表中排除这个子目录 grep -v 在我们注意到快速测试运行后(没有执行命令,或者换句话说,没有第二个 xargs),子目录仍然显示。

在将复杂命令传递给 bash 子层与 bash -c 执行。

最后,我们使用与上面完全相同的方法来构建或命令; cat (显示)文件,移动文件(由 {}) 到子目录,最后删除子目录里面的文件。 我们看到屏幕上显示了 3 个文件(1、2、3)的内容,如果我们查看当前目录,我们的文件就会消失。 我们还可以看到子目录中没有更多文件。 一切正常。

结尾

使用 xargs 成为先进的强大力量 Linux (在这种情况下)bash 用户。 穿着 xargs 结合 bash -c 带来更大的力量; 构建复杂和自定义命令行的独特能力 100% 免费,不需要文件或中间构建,也不需要堆栈/顺序命令。

享受!