首页 > 解决方案 > Bash/GitBash AWK 通过列名而不是列号获取列值(来自命令输出)

问题描述

我安装了最新的 GitBash 版本并且$BASH_VERSION4.4.23(1).

现在我得到这个命令的输出,ps aux如下所示:

PID    PPID    PGID     WINPID   TTY         UID    STIME COMMAND
<4-DIGITS>  <1-DIGIT>  <4-DIGITS>   <4-DIGITS>  ?   <5-DIGITS> <CURR_TIME> <COMMAND>
<4-DIGITS>  <1-DIGIT>  <4-DIGITS>   <4-DIGITS>  ?   <5-DIGITS> <CURR_TIME> <COMMAND>
....
....
<4-DIGITS>  <1-DIGIT>  <4-DIGITS>   <4-DIGITS>  ?   <5-DIGITS> <CURR_TIME> <COMMAND>
<4-DIGITS>  <1-DIGIT>  <4-DIGITS>   <4-DIGITS>  ?   <5-DIGITS> <CURR_TIME> <COMMAND>

从这个输出中,我希望能够通过仅指定列名称(也可以是多个列)来提取特定的列值,而不是每次从左到右计算列号的列。

我有这个命令,但它只适用于文件,但我希望它也适用于另一个命令的输出:

awk -vcol=<COL_NAME> '(NR==1){colnum=-1;for(i=1;i<=NF;i++)if($(i)==col)colnum=i;}{print $(colnum)}'

如何使这项工作用于过滤先前命令的输出,例如ps aux | awk <COLUMN_NAME=WINPID>

标签: awkfilteringmultiple-columnsgit-bash

解决方案


假设:

  • ps输出字段不包含空格(例如,下面的条目STIME看起来不像Sep 27
  • 列名匹配区分大小写(可以通过添加tolower()调用来更改)

示例输入文件:

$ cat ps.out
PID    PPID    PGID     WINPID   TTY         UID    STIME COMMAND
<4-DIGITS1>  <1-DIGIT1>  <4-DIGITS1>   <4-DIGITS1>  ?   <5-DIGITS1> <CURR_TIME1> <COMMAND1>
<4-DIGITS2>  <1-DIGIT2>  <4-DIGITS2>   <4-DIGITS2>  ?   <5-DIGITS2> <CURR_TIME2> <COMMAND2>
<4-DIGITS3>  <1-DIGIT3>  <4-DIGITS3>   <4-DIGITS3>  ?   <5-DIGITS3> <CURR_TIME3> <COMMAND3>
<4-DIGITS4>  <1-DIGIT4>  <4-DIGITS4>   <4-DIGITS4>  ?   <5-DIGITS4> <CURR_TIME4> <COMMAND4>

一个想法使用awk

$ columns='WINPID'
$ awk -v cols="${columns}" '
BEGIN  { n=split(cols,arr,",")            # parse list of column names
         for (i=1;i<=n;i++) 
             headers[arr[i]]              # convert to associative array
       }
FNR==1 { for (i=1;i<=NF;i++)              # for each field (aka column) header ...
             if ($i in headers)           # if it is in headers[] then ...
                fields[i]                 # keep track of the associated field #
       }
       { pfx=""
         for (i=1;i<=NF;i++) {            # for each input field # ...
             if (i in fields) {           # if it is in fields[] then ...
                printf "%s%s", pfx, $i    # print the field (aka column)
                pfx=OFS
             }
         }
         printf "\n"                      # terminate the line
       }
' ps.out

这会产生:

WINPID
<4-DIGITS1>
<4-DIGITS2>
<4-DIGITS3>
<4-DIGITS4>

columns='WINPID,UID'我们得到:

WINPID UID
<4-DIGITS1> <5-DIGITS1>
<4-DIGITS2> <5-DIGITS2>
<4-DIGITS3> <5-DIGITS3>
<4-DIGITS4> <5-DIGITS4>

注意: OP 可以修改printf格式以根据需要调整输出


要将awk脚本直接应用于ps(通过模拟cat ps.out)的输出:

$ columns='PID,STIME,COMMAND'
$ cat ps.out | awk -v cols="${columns}" '
BEGIN  { n=split(cols,arr,",")
         for (i=1;i<=n;i++) 
             headers[arr[i]]
       }
FNR==1 { for (i=1;i<=NF;i++)
             if ($i in headers)
                fields[i]
       }
       { pfx=""
         for (i=1;i<=NF;i++) {
             if (i in fields) {
                printf "%s%s", pfx, $i
                pfx=OFS
             }
         }
         printf "\n"
       }
'

这会产生:

PID STIME COMMAND
<4-DIGITS1> <CURR_TIME1> <COMMAND1>
<4-DIGITS2> <CURR_TIME2> <COMMAND2>
<4-DIGITS3> <CURR_TIME3> <COMMAND3>
<4-DIGITS4> <CURR_TIME4> <COMMAND4>

推荐阅读