在外壳上对列表执行操作


3

我有要卸载的软件包列表。卸载程序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目录中的文件执行操作。

Answers:


5

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

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

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

> 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变量


1
在您的情况下,由于该pkg_deinstall命令允许多个参数,所以简单pkg_info | grep proto | sed 's/([a-z0-9])./\1/' | xargs pkg_deinstall就足够了。
斯特凡希门尼斯

是的,这就是我所需要的。我知道了 非常感谢你!
EhevuTov 2011年

2
避免解析输出ls以作用于文件。请使用诸如或的外壳gloglo 。在这个OP的问题中,他们已经有一个生成的软件包列表,因此将其管道传递到while读取循环中可能没问题,但是对于文件名则不行!for file in *; do [...] donefind -exec
卡莱布

@Caleb,只要我用sed做正则表达式就可以了吗?
EhevuTov 2011年

@Stephane:为什么不发表您的评论,避免出现问题ls
用户未知,

3

将输出流转换为参数列表的主要方法有两种。快速(有时是肮脏)的方法是使用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]我使用过{},因为find和其他类似的exec函数使用这种格式,但是任何唯一的字符串都可以。
[2]该pkg_deinstall命令没有在模式之后出现的任何参数,因此这仅是示例!
[3]我-n在所有示例中都添加了内容,因此,如果您复制并粘贴代码,则它实际上不会执行任何操作,仅向您显示测试运行。


1

不要无视其他答案...这只是另一种实现方式:

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

例如,报价是一场噩梦,因此我个人倾向于使用| while read foo ...更多。


这样做while IFS= read -r foo; do …-r以禁用反斜杠的特殊处理并IFS=避免剥离初始空格)。
吉尔(Gilles)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.