【问题标题】:bash: shortest way to get n-th column of outputbash:获得第 n 列输出的最短方法
【发布时间】:2011-11-11 01:11:42
【问题描述】:

假设在您的工作日中,您在 bash 中的某个命令反复遇到以下形式的列输出(在我的情况下,是在我的 Rails 工作目录中执行 svn st):

?       changes.patch
M       app/models/superman.rb
A       app/models/superwoman.rb

为了使用您的命令的输出 - 在这种情况下是文件名 - 需要进行某种解析,以便第二列可以用作下一个命令的输入。

我一直在做的是使用awk 来获取第二列,例如当我想删除所有文件时(不是典型的用例 :),我会这样做:

svn st | awk '{print $2}' | xargs rm

由于我输入了很多,一个自然的问题是:在 bash 中是否有更短(因此更酷)的方式来完成此操作?

注意: 即使我的具体示例在我的 svn 工作流程中,我要问的本质上是一个 shell 命令问题。如果您觉得工作流程很愚蠢并建议了另一种方法,我可能不会投票给您,但其他人可能会,因为这里的问题实际上是如何以最短的方式在 bash 中获取第 n 列命令输出.谢谢:)

【问题讨论】:

  • 当您经常使用命令时,您最好创建一个脚本并将其放在您的路径中。如果您愿意,可以简单地在 bashrc 中创建一个函数。我看不出减少列选择表达式的意义。
  • 你是对的,我可能会这样做。 “重点”是寻求在 bash 中做事的新方法,目的是为了学习,但主要是为了好玩:)
  • 另外你在 ssh-ing 的时候没有你的 .bashrc,所以在没有它的情况下了解你的方式很有用。

标签: bash awk


【解决方案1】:

您可以使用cut 访问第二个字段:

cut -f2

编辑: 抱歉,没有意识到 SVN 在其输出中不使用制表符,所以这有点没用。您可以将 cut 定制到输出,但它有点脆弱 - 像 cut -c 10- 这样的东西可以工作,但确切的值将取决于您的设置。

另一个选项类似于:sed 's/.\s\+//'

【讨论】:

  • 尝试使用cut -f2svn st 输出。你会发现它不起作用。
  • 将适当的分隔符传递给-d 允许它工作。
  • 为了扩展@Yogh 所说的内容,对于作为分隔符的空格,它看起来像cut -d" " -f2
  • 在标准输出上没有 awk 使用 xargs:svn st | xargs |剪切 -d" " -f2
  • Thnx,这是我的可用空间使用情况:df -h / | tail -n1 | xargs | cut -d" " -f4
【解决方案2】:

完成与以下相同的事情:

svn st | awk '{print $2}' | xargs rm

只使用你可以使用的 bash:

svn st | while read a b; do rm "$b"; done

当然,它不是更短,但它更高效,它可以正确处理文件名中的空格。

【讨论】:

  • ab 是什么,如何获得?
  • @Timo a 代表第一列,b 代表其余列。如果要打印第二列,请使用read a b c;,然后使用echo 而不是rm。我用它来获取一堆遵循 grep 模式的 ID 进程,因此我可以将它们全部中断。
  • 实际上,shell 中的while read 循环往往比使用外部实用程序。参见例如stackoverflow.com/questions/13762625/…
【解决方案3】:

我发现自己处于同样的情况,最终将这些别名添加到我的 .profile 文件中:

alias c1="awk '{print \$1}'"
alias c2="awk '{print \$2}'"
alias c3="awk '{print \$3}'"
alias c4="awk '{print \$4}'"
alias c5="awk '{print \$5}'"
alias c6="awk '{print \$6}'"
alias c7="awk '{print \$7}'"
alias c8="awk '{print \$8}'"
alias c9="awk '{print \$9}'"

这让我可以这样写:

svn st | c2 | xargs rm

【讨论】:

  • Bash 函数通常更有用。我这样做了:function c() { awk "{print \$$1}" } 然后你可以这样做:svn st | c 2 | xargs rm
  • 但是我需要输入一个额外的空格。太累了:)
  • 你可以写一个 bash 函数来为你增加额外的空间:P
【解决方案4】:

试试 zsh。它支持后缀别名,因此您可以在 .zshrc 中将 X 定义为

alias -g X="| cut -d' ' -f2"

那么你可以这样做:

cat file X

您可以更进一步,为第 n 列定义它:

alias -g X2="| cut -d' ' -f2"
alias -g X1="| cut -d' ' -f1"
alias -g X3="| cut -d' ' -f3"

这将输出文件“file”的第n列。您也可以对 grep 输出或更少输出执行此操作。这非常方便,是 zsh 的杀手级功能。

您可以更进一步,将 D 定义为:

alias -g D="|xargs rm"

现在您可以输入:

cat file X1 D

删除文件“文件”第一列中提到的所有文件。

如果您了解 bash,那么除了一些新功能之外,zsh 并没有太大的变化。

HTH 克里斯

【讨论】:

  • 啊,我看到你在我上面输入我的评论时更新了你的答案:) 你能以某种方式动态指定要获取的列,还是我的 .zshrc 中需要 n 行(不是这很重要,只是好奇)
  • 我已经进一步编辑了我的帖子并定义了一个后缀“D”来删除文件。据我所知,您必须为每个后缀添加一行。
  • 或者让它成为X命令的第一个参数。这些都不需要 zsh 或别名,除了将管道放在别名中的特殊想法。
  • 我不明白为什么你们似乎都喜欢cut 选项。使用svn st 的输出,它根本无法在我的机器上运行。试试svn st | cut -d' ' -f2 看看会发生什么。
  • @Sv1 你可以编写一个循环来定义别名,因为别名只是一个命令。
【解决方案5】:

因为您似乎对脚本不熟悉,所以这里举个例子。

#!/bin/sh
# usage: svn st | x 2 | xargs rm
col=$1
shift
awk -v col="$col" '{print $col}' "${@--}"

如果您将其保存在 ~/bin/x 中并确保 ~/bin 在您的 PATH 中(现在您可以并且应该将其放入您的 .bashrc 中),那么您通常可以使用最短的命令来提取列 n ; x n.

如果使用非数字参数或参数数量不正确等调用,脚本应该进行适当的错误检查和保释;但是在单元 102 中扩展这个基本的基本版本。

也许您会想要扩展脚本以允许使用不同的列分隔符。默认情况下,awk 将输入解析为空格上的字段;要使用不同的分隔符,请使用-F ':',其中: 是新的分隔符。将此作为脚本的一个选项来实现会使它稍长一些,因此我将其作为练习留给读者。


用法

给定一个文件file

1 2 3
4 5 6

您可以通过 stdin 传递它(使用 useless cat 仅作为更有用的占位符);

$ cat file | sh script.sh 2
2
5

或者将其作为参数提供给脚本:

$ sh script.sh 2 file
2
5

这里sh script.sh假设脚本在当前目录下保存为script.sh;如果您在 PATH 的某处使用更有用的名称保存它并将其标记为可执行,如上面的说明,显然使用有用的名称代替(而不是 sh)。

【讨论】:

  • 好主意,但对我来说并不像在 sh 或 bash 上那样工作。这有效:#!/bin/bash col=$1 shift awk "{print \$$col}"
  • 感谢您迟到的评论。更新了你的修复。
  • 更好awk -v col=$col ...
  • @fedorqui 感谢您在这里和其他地方的反馈!更新了答案。它现在稍长一些,因此如果您想要绝对最短的脚本,请参阅 revision history 以获取原始版本。
  • @fedorqui 非常感谢!出于歇斯底里的原因,在 cat 示例中添加了一个小警告 (-:
【解决方案6】:

看来您已经有了解决方案。为了让事情变得更简单,为什么不把你的命令放在一个 bash 脚本中(用一个短名称)然后运行它而不是每次都输入那个“长”命令?

【讨论】:

  • 在我看来,它只是不那么“酷”。为什么?好吧,我不想让我的 .bashrc 充斥着各种执行此操作的快捷方式,本质上是编写一个元 shell。它的别名非常好。话虽如此,您的建议还不错。
  • .bashrc?你为什么要用非 bash 相关的东西把它弄得乱七八糟。我所做的是为每组“命令”编写单独的脚本,并将它们全部放在~/scripts 目录中。然后将整个 ~/scripts 添加到我的 PATH 中,这样我可以在需要时直接调用它们。
  • 那么不要把它放在你的.bashrc中。在 ~/bin 中创建一个脚本并确保它在您的 PATH 中。
【解决方案7】:

如果您可以手动选择列,使用pick 可以非常快:

svn st | pick | xargs rm

只需转到第二列的任何单元格,按c,然后点击enter

【讨论】:

  • 我试用了您提到的“pick”工具,我非常喜欢它。在很多情况下,我想打印选定的列但不想输入“awk '{print $3}'”来获取所需的列,这非常有用。不幸的是,该名称与另一个似乎更流行的“pick”工具冲突——它可以使用“apt install pick”安装。所以我在我的系统上将你的工具重命名为“pickk”,并打算继续使用它。感谢您发布参考。
【解决方案8】:

注意,文件路径不必在 svn st 输出的第二列。例如,如果您修改文件,并修改它的属性,它将是第 3 列。

查看可能的输出示例:

svn help st

示例输出:

 M     wc/bar.c
A  +   wc/qax.c

我建议通过以下方式剪切前 8 个字符:

svn st | cut -c8- | while read FILE; do echo whatever with "$FILE"; done

如果你想 100% 确定,例如处理带有空格的花哨文件名,你需要解析 xml 输出:

svn st --xml | grep -o 'path=".*"' | sed 's/^path="//; s/"$//'

当然,您可能想使用一些真正的 XML 解析器而不是 grep/sed。

【讨论】:

    猜你喜欢
    • 2019-10-11
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 2022-08-10
    • 1970-01-01
    • 1970-01-01
    • 2019-07-09
    • 2010-12-02
    相关资源
    最近更新 更多