如何在AWK中打印$1、$2和所有剩余参数的组合

3

我正在制作一个从基础文件生成别名/缩写的脚本。基础文件的结构类似于:

sctl   sudo         systemctl
pac    sudo         pacman

这段代码可正常运行,它读取基础文件,并删除注释,接着在缩写文件中利用awk命令找到缩写行。
    sed "s/\s*#.*$//;/^\s*$/d" $command_file | 
    awk -v c=$cmd -v o="$comp" '{ print c" "$1""o"\""$2" "$3"\"" }' >> $file

最终结果将是这样的:
abbr sctl "sudo systemctl"
abbr pac "sudo pacman"

但是,当一行代码的第3个参数后面有很多部分时,这段代码就无法正常工作:

svu    playerctl    -p spotify volume +0.05

我该如何以此格式进行打印?$1 $2 ($3..$N)

1
请参见 https://dev59.com/foLba4cB1Zd3GeqPisDT。 - Wiktor Stribiżew
我不知道它如何适用于这种情况,因为我需要在开始时打印别名或缩写,即c变量(对于zsh或fish),并且在$1之后,我需要放置“ ”或“ =”(再次针对fish或zsh),这是o变量,并且在最后一个参数之前和之后加上引号。 - Onizudo
你可以只保留awk代码块,对不以注释开头或至少有这么多字段的行进行处理,而不需要使用sed组件。 - Rorschach
更简单的方法是只需保留一个aliases文件并在.bashrc中引用它。建议创建单独的块来测试if [ "$UID" -eq '0' ]; then alias pms='pacman -S --needed'; alias pmsu='pacman -Syu'; ... else alias pms='sudo pacman -S --needed'; alias pmsu='sudo pacman -Syu'; ... fi - David C. Rankin
5个回答

2
最初的回答:使用 awk 时,您永远不需要 sed。给定此输入文件:

当您使用 awk 时,不需要使用 sed。给定以下输入文件:

$ cat file
sctl   sudo         systemctl   # here is a comment
        # and here is another
pac    sudo         pacman

svu    playerctl    -p spotify volume +0.05

以下是你的sed+awk输出结果:

最初的回答:

$ sed "s/\s*#.*$//;/^\s*$/d" file |  awk -v c="abbr" -v o=" " '{ print c" "$1""o"\""$2" "$3"\"" }'
abbr sctl "sudo systemctl"
abbr pac "sudo pacman"
abbr svu "playerctl -p"

你可以仅使用awk获得它的最初回答。
$ awk -v c="abbr" -v o=" " '{sub(/\s*#.*/,"")} NF{print c" "$1""o"\""$2" "$3"\""}' file
abbr sctl "sudo systemctl"
abbr pac "sudo pacman"
abbr svu "playerctl -p"

最初的回答是,您可以调整它以实现您想要的功能:

并使其符合您的需求:

$ awk -v c="abbr" -v o=" " '{sub(/\s*#.*/,"")} NF{x=$1; sub(/^\S+\s+/,""); $1=$1; print c, x o "\"" $0 "\""}' file
abbr sctl "sudo systemctl"
abbr pac "sudo pacman"
abbr svu "playerctl -p spotify volume +0.05"

"最初的回答":根据你的问题,我无法确定你想在双引号内外要什么。
$ awk -v c="abbr" -v o=" " '{sub(/\s*#.*/,"")} NF{x=$1" "$2; sub(/^(\S+\s+){2}/,""); $1=$1; print c, x o "\"" $0 "\""}' file
abbr sctl sudo "systemctl"
abbr pac sudo "pacman"
abbr svu playerctl "-p spotify volume +0.05"

以上代码使用GNU awk的\s\S - 对于其他awk使用[[:space:]][^[:space:]]代替。

由于我们正在使用GNU awk,因此可以使用match()的第三个参数更简洁、高效地完成工作:

原始答案:Original Answer

$ awk -v c="abbr" -v o=" " '{sub(/\s*#.*/,""); $1=$1} match($0,/(\S+) (.*)/,a){print c, a[1] o "\"" a[2] "\""}' file
abbr sctl "sudo systemctl"
abbr pac "sudo pacman"
abbr svu "playerctl -p spotify volume +0.05"

$ awk -v c="abbr" -v o=" " '{sub(/\s*#.*/,""); $1=$1} match($0,/(\S+ \S+) (.*)/,a){print c, a[1] o "\"" a[2] "\""}' file
abbr sctl sudo "systemctl"
abbr pac sudo "pacman"
abbr svu playerctl "-p spotify volume +0.05"

1
我稍后会尝试这个,谢谢你给了我新的视角,我以为需要用sed来完成注释部分。点赞 =D - Onizudo

2
你可以删除前两个字段并修剪其余部分的空格,例如:
最初的回答:

您可以删除前两个字段,并从其余部分修剪空格,例如。

{ 
  printf "%s %s ", $1, $2
  $1=$2=""; sub(/^\s*/, "", $0);
  printf "\"%s\"\n", $0
}

With output like,

svu playerctl "-p spotify volume +0.05"

注意:正则表达式\s需要使用gnu awk,正如Ed Morton所指出的那样。最初的回答。

1
替换掉多余的两个字段。
$ echo "svu playerctl -p spotify volume +0.05" | gawk '
  { print $1; $1 = ""
    print $2; $2 = ""
    print substr($0,3) } '
svu
playerctl
-p spotify volume +0.05
< p > substr 函数从第一和第二个字段的剩余部分中删除输出字段分隔符。


1
这是一个awk脚本(标准Linux-特定于gawk),可以一次完成所有工作:
awk -v c="abbr" -v o=" " '/(^\s*$)|(^\s*#.*$)/ {next}  # discard empty lines and comments
{
    arg3=$0;                          # save current line to arg3 variable
    sub($1" "$2,"",arg3);             # remove $1  and $2 from arg3
    print(c" "$1""o"\""$2" "arg3"\"") # print formated output
}' input.txt

input.txt

ctl   sudo         systemctl
# comment line
pac    sudo         pacman
   # comment line
demo1  sudo         arg1 arg2 arg3

demo2  sudo         arg4 -arg5 -arg6 456
                         # comment line

输出:

abbr ctl "sudo          systemctl"
abbr pac "sudo          pacman"
abbr demo1 "sudo          arg1 arg2 arg3"
abbr demo2 "sudo          arg4 -arg5 -arg6 456"

未关闭 ' 的症状。测试您的输入文件 cat input.txt,添加一个终止新行。 - Dudi Boy
在删除旧内容时,要记得移除函数的结尾} =P - Onizudo
感谢,如果您感到满意,请通过点赞表达您的赞赏。 - Dudi Boy
1
谢谢,不需要删除行末的注释。感谢您的$1评论,非常正确。根据当前问题,$1和$2都是纯字母。 - Dudi Boy
从行末删除注释也不是必需的 :-). OP 的 sed 脚本可以处理独立的注释或在行末的注释,所以如果你正在处理其中之一... 是的,我知道这些示例只是字母表,但谁知道真实文件中还有什么其他内容,避免在 regexp 上下文中使用输入字符串(除非你有非常特定的需求)是一个好习惯。 - Ed Morton
显示剩余2条评论

0

这是 GNU sed 版本:

sed -r 's/(^[^ ]+)\s+([^ ]+)\s+(.*)/abbr \1 "\2 \3"/g' base_file
abbr sctl "sudo systemctl"
abbr pac "sudo pacman"
abbr svu "playerctl -p spotify volume +0.05"

解释:这里使用了回溯引用(back-referencing),每列被捕获到相应的组中,稍后作为\1\2\3使用。请注意,上述sed命令中使用了-r标志以启用扩展正则表达式。如果目标机器中没有此标志,则可以使用以下命令:

sed  's/\(^[^ ]\+\)\s\+\([^ ]\+\)\s\+\(.*\)/abbr \1 "\2 \3"/g' base_file
abbr sctl "sudo systemctl"
abbr pac "sudo pacman"
abbr svu "playerctl -p spotify volume +0.05"

这两个命令的唯一区别在于后者中的()+被转义了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接