为什么双引号在 awk 脚本中 cmd 内的 echo 语句中不起作用?

Why double quote does not work in echo statement inside cmd in awk script?

gawk 'BEGIN { FS="|"; OFS="|" }NR ==1 {print} NR >=2 {cmd1="echo -n "" | base64 -w 0";cmd1 | getline d1;close(cmd1); print ,d1 }' dummy2.txt

输入:

id|dummy                                                                           
1|subhashree:1;user=phn                                                             
2|subha:2;user=phn        

                                                                                                            

预期输出:

id|dummy                                       
1|c3ViaGFzaHJlZToxO3VzZXI9cGhuCg==                               
2|c3ViaGE6Mjt1c2VyPXBobgo= 

                                           

脚本产生的输出:

id|dummy                                       
1|subhashree:1                                                                                                                                                                                                                                                                                                                                                   
2|subha:2     

                                                                                                   

我明白是 $2 左右的双引号导致了这个问题。它不起作用,因此没有正确编码字符串,只是在 semi colon.Because 之后剥离字符串它确实在分号内工作并在终端中提供正确的输出。

echo "subhashree:1;user=phn" | base64                                               
c3ViaGFzaHJlZToxO3VzZXI9cGhuCg==                                                         
[root@DERATVIV04 encode]# echo "subha:2;user=phn" | base64                               
c3ViaGE6Mjt1c2VyPXBobgo=        

                                                      

我已经尝试在 awk 中使用单引号和双引号进行不同的变体,但它没有work.Any帮助将不胜感激。

非常感谢。

您现有的cmd1生产

echo -n subhashree:1;user=phn | base64 -w 0

                    ^ semicolon is there

所以如果你执行下面会产生

$ echo -n subhashree:1;user=phn | base64 -w 0
subhashree:1

带引号

$ echo -n 'subhashree:1;user=phn' | base64 -w 0
c3ViaGFzaHJlZToxO3VzZXI9cGhu

解决方法就是在 echo -n '<your-string>' | base64 -w 0

之前使用引号
$ cat file 
id|dummy
1|subhashree:1;user=phn
2|subha:2;user=phn

$ gawk -v q="'" 'BEGIN { FS="|"; OFS="|" }NR ==1 {print} NR >=2 {cmd1="echo -n " q  q" | base64 -w 0";  cmd1 | getline d1;close(cmd1); print ,d1 }' file
id|dummy
1|c3ViaGFzaHJlZToxO3VzZXI9cGhu
2|c3ViaGE6Mjt1c2VyPXBobg==

可以简化如下

gawk -v q="'" 'BEGIN {
                 FS=OFS="|"
               }
               NR==1{
                  print;
                  next
               }
               {
                 cmd1="echo -n " q  q" | base64 -w 0";
                 print ((cmd1 | getline d1)>0)?  OFS d1 : [=14=];
                 close(cmd1);
               }
               ' file

基于Ed Morton recommendation http://awk.freeshell.org/AllAboutGetline

if/while ( (getline var < file) > 0)
if/while ( (command | getline var) > 0)
if/while ( (command |& getline var) > 0)

问题是因为在 shell 上下文中尝试 运行 echo 命令时缺少引号。你要做的基本上是转换成

echo -n subhashree:1;user=phn | base64 -w 0

shell 执行的两个命令由 ; 分隔,即 user=phn | base64 -w 0 表示赋值后跟管道,管道为空,因为赋值不会产生任何结果base64 的标准输入用于编码。另一段 subhashree:1 只是回显出来,它存储在您的 getline 变量 d1.

解决问题的正确方法应该是使用引号

echo -n "subhashree:1;user=phn" | base64 -w 0

当你说你在 </code> 上使用引号时,这实际上是不正确的,引号实际上是在 <code>awk 的上下文中用于连接 cmd 字符串,即"echo -n "</code> 和 <code>" | base64 -w 0" 只是连接在一起。建议的双引号需要在 shell.

的上下文中

所以有了那个和其他一些修复,你的 awk 命令应该在下面。添加 gsub() 以删除尾随空格,这些空格出现在您显示的输入中。还使用了 printf 而不是回显。

awk -v FS="|" '
    BEGIN {
        OFS = FS
    }
    
    NR == 1 {
        print
    }
    
    NR >= 2 {
        gsub(/[[:space:]]+/, "", )
        cmd = "printf \"%s\" \""  "\" | base64 -w 0"
        if ((cmd | getline result) > 0) {
             = result
        }
        close(cmd)
        print
    }    
' file

所以对于上面的命令,你的命令执行如下,这将产生正确的结果。

printf "%s" "subhashree:1;user=phn" | base64 -w 0

您已经得到解释如何为此使用 awk 的答案,但您还应该考虑不为此使用 awk。排序调用其他命令(例如 bas64)的工具是 shell,而不是 awk。您在调用方面尝试做的是:

shell { awk { loop_on_input { shell { base64 } } } }

而如果您直接从 shell 调用 base64,它只是:

shell { loop_on_input { base64 } }

请注意,awk 命令每行输入生成一个新的子shell一次,而来自 shell 的直接调用则不会。

例如:

#!/usr/bin/env bash

file='dummy2.txt'
head -n 1 "$file"
while IFS='|' read -r id dummy; do
    printf '%s|%s\n' "$id" "$(base64 -w 0 <<<"$dummy")"
done < <(tail -n +2 "$file")

这是由 awk -v n=100 'NR==1{print; next} {for (i=1;i<=n;i++) print}' dummy2.txt > file100

创建的每条数据行重复 100 次的输入文件的执行速度差异
$ ./tst.sh file100
Awk:

real    0m23.247s
user    0m3.755s
sys     0m10.966s

Shell:

real    0m14.512s
user    0m1.530s
sys     0m4.776s

以上时间是由运行这个命令产生的(答案中发布的两个awk脚本的时间大致相同,所以我只是随机选择了一个):

#!/usr/bin/env bash

doawk() {
    local file=""
    gawk -v q="'" 'BEGIN {
                 FS=OFS="|"
               }
               NR==1{
                  print;
                  next
               }
               {
                 cmd1="echo -n " q  q" | base64 -w 0";
                 print ((cmd1 | getline d1)>0)?  OFS d1 : [=14=];
                 close(cmd1);
               }
               ' "$file"
}

doshell() {
    local file=""
    head -n 1 "$file"
    while IFS='|' read -r id dummy; do
        printf '%s|%s\n' "$id" "$(base64 -w 0 <<<"$dummy")"
    done < <(tail -n +2 "$file")
}

# Use 3rd-run timing to eliminate cache-ing as a factor

doawk "" >/dev/null
doawk "" >/dev/null
echo "Awk:"
time doawk "" >/dev/null

echo ""

doshell "" >/dev/null
doshell "" >/dev/null
echo "Shell:"
time doshell "" >/dev/null