从 shell 对列表执行操作

从 shell 对列表执行操作

我有一个要卸载的软件包列表。卸载程序pkg_deinstall不将软件包列表作为卸载参数。我如何从列表中卸载(如 foreach 循环)?

[root@fbsd01 /usr/ports/editors/vim]# pkg_info | grep proto| sed 's/\([a-z0-9]*\).*/\1/'
bigreqsproto
compositeproto
damageproto
fixesproto
fontsproto
inputproto
kbproto
randrproto
renderproto
xcb
xcmiscproto
xextproto
xf86bigfontproto
xineramaproto

我认为与此类似的命令可以工作,但我必须将列表作为参数而不是流传递:pkg_info | grep proto| sed 's/\([a-z0-9]*\).*/\1/' | head -n 1 pkg_deinstall

如果您能给我一些关于使用什么程序和语法的线索,那将会很有帮助。我知道,由于它是独一无二的,您可能很难得出准确的答案。如果我的问题太复杂,也许有人可以告诉我如何对ls目录中的文件执行操作。

答案1

对于最基本的“foreach” xargs,它读取一些参数并将它们附加到其他命令行。例如您可以尝试:

> cut -d: -f1 /etc/passwd | xargs -n1 echo user:

请注意,如果用户名可能包含空格字符,您应该传递一个-d'\n'参数。xargs要将所有 1 参数附加到同一命令,请使用:

> cut -d: -f1 /etc/passwd | xargs echo users:

对于更一般的循环,您可以使用:

> cut -d: -f1 /etc/passwd | while read i; do echo $i; done

这次,$i可以将其放置在一个或多个命令序列中的任何位置。输入一次读取一行,并存储在变量 中i。也可以拆分²行并将其存储在多个变量中:

> cut -d: -f1,6 /etc/passwd --output-delimiter=' ' | \
  while read i j; do echo home directory of user $i is $j; done

请注意,对于原始文件名的循环,以下内容更好(请参阅注释):

> for i in *.txt; do echo file: $i; done


1. 更准确地说:系统允许的数量。
2.根据IFS变量

答案2

将输出流转换为参数列表有两种主要方法。执行此操作的快速(有时是肮脏的)方法是使用xargs.默认情况下,它接受标准输入流,并以输入的每一行作为单独的参数运行命令。您应该始终注意输入的格式,因为当作为参数传递时,空格和其他字符很容易被误解。然而,包名称相对安全。它们不应包含 shell 通配符或换行符或任何疯狂的内容,因此您的命令如下所示:[3]

$ pkg_info | grep proto | sed 's/\([a-z0-9]*\).*/\1/' | xargs pkg_deinstall -n

常见的情况是,您实际上需要为正在处理的每一行运行一次该命令。在这种情况下,-n论证就派上用场了。您可以-n1一次运行一个,甚至-n10可以十个为一批运行它们。

$ [pipline] | xargs -n1 pkg_deinstall -n

现在假设您需要在自动生成的一次之后添加另一个参数。在这种情况下,您可以使用“-I”指定一个字符串来替换自动生成的参数。这还允许您在必要时进行诸如引用论证之类的操作。然后,您可以格式化参数的放置位置,如下所示:[1][2]

$ [pipline] | xargs -n1 -I{} pkg_deinstall -n "{}" --trailing-argument

如果您想要更多控制或为每行输入运行多个命令,您的下一个选择是使用如下循环:

$ [pipline] | while read line; do
      echo "Uninstalling $line..."
      pkg_deinstall -n "$line"
  done

请注意,它会pkg_deinstall自动使用表达式作为部分匹配 glob,因此请注意您传递的要卸载的内容。传递某些系统组件的基本名称不仅会卸载该组件,还会卸载具有类似名称或依赖于它的所有内容!这可能是灾难性的,因此请务必仔细检查您的套件。您可以用来-n进行试运行,以显示将要完成的操作,而无需实际执行[3]和/或-i以交互方式运行每个步骤。

[1] 我使用{}for 因为 find 和其他类似的 exec 函数使用这种格式,但任何唯一的字符串都可以。
[2] 该pkg_deinstall命令在模式之后没有任何参数,因此这只是一个示例!
[3] 我添加-n到所有示例中,这样如果您复制并粘贴代码,它实际上不会执行任何操作,只是向您展示测试运行。

答案3

不要忽略其他答案......这只是另一种方法:

for pkg in $( pkg_info | grep proto| sed 's/\([a-z0-9]*\).*/\1/' ) ; do
    pkg_deinstall $pkg
done

引用在示例中是一场噩梦,所以我个人倾向于| while read foo ...更频繁地使用。

相关内容