从文件传递时变量在 awk 中不接受

从文件传递时变量在 awk 中不接受

我正在逐行读取文件。每行看起来像这样:

xxyu: JHYU_IOPI

每行都传递给 awk,如下所示。我想打印匹配模式的上一行;我可以用 grep 来实现这一点,并想知道我在 awk 中哪里犯了错误。

#!/bin/bash
while read i
do
 awk '/$i/{print a}{a=$0}' ver_in.txt
done<in.txt

我也尝试过这个:

#!/bin/bash
while read i
do
 awk -v var="$i" '/var/{print a}{a=$0}' jil.txt
done<in.txt

编辑:在得到建议不要使用 sh read 后使用 awk 。我的输入和期望的输出如下所示:

编辑 1:编辑@Ed Morton awk 脚本的输入,如下所示

输入文件:cat文件

/* ----------------- AIX_RUN_WATCH ----------------- */ 

insert_job: AIX_RUN_WATCH   job_type: BOX 
owner: root
permission: 
date_conditions: 1
days_of_week: su
start_times: "22:00"
alarm_if_fail: 1
alarm_if_terminated: 1
group: app
send_notification: 0
notification_emailaddress: 


 /* ----------------- AIX_stop ----------------- */ 

 insert_job: AIXstop   job_type: CMD 
 box_name: AIX_RUN_WATCH
 command: ls
 machine: cfg.mc
 owner: root
 permission: 
 date_conditions: 0
 box_terminator: 1
 std_out_file: ">> /tmp/${AUTOSERV}.${AUTO_JOB_NAME}.$(date +%Y%m%d).stdout"
 std_err_file: ">> /tmp/${AUTOSERV}.${AUTO_JOB_NAME}.$(date +%Y%m%d).stderr"
 alarm_if_fail: 1
 alarm_if_terminated: 1
 group: app
 send_notification: 1


 /* ----------------- AIX_start ----------------- */ 

 insert_job: AIX_start   job_type: CMD 
 box_name: AIX_RUN_WATCH
 command: ls
 machine: cfg.mc
 owner: root
 permission: 
 date_conditions: 0
 box_terminator: 1
 std_out_file: ">> /tmp/${AUTOSERV}.${AUTO_JOB_NAME}.$(date +%Y%m%d).stdout"
 std_err_file: ">> /tmp/${AUTOSERV}.${AUTO_JOB_NAME}.$(date +%Y%m%d).stderr"
 alarm_if_fail: 1
 alarm_if_terminated: 1
 group: app

   cat targets
     box_name: AIX_RUN_WATCH

预期产出 -

 box_name: AIX_RUN_WATCH
 insert_job: AIX_stop
 insert_job: AIX_start

答案1

第一次尝试时,您需要使用双引号进行 shell 变量扩展,然后对 awk$运算符进行转义,以防止它被 shell 扩展,但请注意,如果变量$i包含特殊字符(如\, ),这样使用会破坏 awk /。 [我现在将跳过修复您的命令的一个或多个其他问题]。

while read i
do
 awk "/$i/{print a}{a=\$0}" ver_in.txt
done<in.txt

对于第二次尝试,您需要对当前行使用正则表达式匹配或字符串匹配,例如使用正则表达式匹配(部分正则表达式匹配):

while read i
do
 awk -v var="$i" '$0 ~ var{print a}{a=$0}' jil.txt
done<in.txt

或字符串匹配(完整字符串匹配)如:

while read i
do
 awk -v var="$i" '$0==var{print a}{a=$0}' jil.txt
done<in.txt

现在,谈论您尝试使用它们来打印匹配模式的上一行的命令,您可以使用 awk 完成所有操作,然后使用 shell 循环停止;这里我们正在进行完整的字符串匹配:

awk 'NR==FNR { str[$0]; next }
($0 in str) && prev!="" { print prev } { prev=$0 }' in.txt ver_in.txt

或进行部分正则表达式匹配:

awk 'NR==FNR { patt[$0]; next }
{ for(ptrn in patt) if($0 ~ ptrn && prev!="") print prev; prev=$0 }' in.txt ver_in.txt

或进行部分字符串匹配:

awk 'NR==FNR { strings[$0]; next }
{ for(str in strings) if(index($0, str) && prev!="") print prev; prev=$0 }' in.txt ver_in.txt

或进行完整的正则表达式匹配:

awk 'NR==FNR { patt[$0]; next }
{ for(ptrn in patt) if($0 ~ "^"ptrn"$" && prev!="") print prev; prev=$0 }' in.txt ver_in.txt

答案2

为此,您不需要 while 读取循环,并且在 sh 中进行文本处理是一个坏主意(请参阅为什么使用 shell 循环处理文本被认为是不好的做法?)。

相反,让您的 awk 脚本来处理这两个文件。

awk 'NR==FNR { re = $0 "|" re ; next}; # append input line and | to re
     FNR == 1 { sub(/\|$/,"",re) };    # remove trailing | on 1st line of 2nd file

     $0 ~ re { print a }; # if the current line matches re, print a
     {a = $0}' in.txt ver_in.txt

在读取第一个文件 ( in.txt) 时,它在一个变量中构建一个正则表达式,re通过附加每个输入行和正则表达式“交替”来调用(即或者) 操作员。

当它完成读取第一个文件时,它需要的第一件事就是|删除re.这是必要的,因为re总是|由于其构造方式而最终具有性格。如果我们不删除它,该尾随|将导致正则表达式与ver_in.txt.

之后,a如果当前输入行与变量中的正则表达式匹配,则打印变量re(如果 ver_in.txt 的第一行匹配,这将打印一个空行re- 因为 a 是空的。如果您不希望发生这种情况,请将该行从$0 ~ re {print a}$0 ~ re && a != "" {print a})。

然后,无论匹配与否,都设置a=$0

注意:这NR==FNR {... ; next}是一种非常常见的 awk 习惯用法,用于以与第二个和后续输入文件不同的方式处理第一个输入文件。 NR是正在读取的所有文件的全局行计数器,并且FNR是当前文件的行计数器......所以如果NR==FNR,这意味着我们正在读取第一个文件。该next语句跳到下一个输入行,防止 awk 脚本的其余部分在第一个文件中执行。

您没有提供完整的数据样本,所以我自己做了一个测试:

$ cat in.txt 
xxyu: JHYU_IOPI
foo
bar

这个 in.txt 文件将导致 re 等于bar|foo|xxyu: JHYU_IOPI

顺便说一句,因为 awk 脚本正在对 进行正则表达式匹配re,所以 中的行in.txt被视为正则表达式,而不是固定文本。这意味着,如果您希望 in.txt 中的任何正则表达式特殊字符(例如.|[])被视为文字字符,您需要用反斜杠转义它们......您必须这样做这也与您原来的 sh+awk 循环一起使用。

$ cat ver_in.txt 
a line 1
xxyu: JHYU_IOPI
b line 3
d line 4
bar
e line 6
f line 7
foo

上面 awk 脚本的输出:

a line 1
d line 4
f line 7

答案3

不要使用 shell 循环来操作文本,请参阅为什么使用 shell 循环处理文本被认为是不好的做法?。发明shell的人还发明了awk,供shell调用来操作文本。

在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ cat tst.awk
NR==FNR {
    tgts[$0]
    next
}
$0 in tgts {
    if ( $0 != prevTgt ) {
        print $0
        prevTgt = $0
    }
    print prevLine
}
{ prevLine = $1 FS $2 }

$ awk -f tst.awk targets file
box_name: AIX_RUN_WATCH
insert_job: AIXstop
insert_job: AIX_start

原答案:

awk '
    BEGIN { RS=""; FS="\n" }
    $2 != prev {
        print $2
        prev = $2
    }
    { print $1 }
' file
ght: ertyjk
xxx: rtyuiol
xxx: ertyuikl_fghjk
xxx: qwertyujkl
xxx: rtyuiol_123
ght: YUIOPO
xxx: rtyuiol
xxx: rtyuiopfghj
xxx: dfghjkvbnm
xxx: qzdfghnbvfgh
xxx: qsxcvghuiokmnhgf

https://www.gnu.org/software/gawk/manual/gawk.html#Multiple-Line了解如何将 RS 设置为 null 让我们处理多行记录,然后将 FS 设置为换行符意味着此类记录中的每个字段都是一整行,因此我们将您的数据视为空行分隔的记录,每个记录包含2行数据。

您提到有一些其他的 ght 行文件指示应打印哪些行,这意味着还有其他不应打印的块。如果你有这样一个文件,它看起来像这样:

$ cat targets
ght: ertyjk
ght: YUIOPO

并且您的其他输入文件包含一些与ght:上述内容不匹配的行,例如,请参阅ght: whatever下面修改后的输入文件中的块:

$ cat file
xxx: rtyuiol
ght: ertyjk

xxx: ertyuikl_fghjk
ght: ertyjk

xxx: qwertyujkl
ght: ertyjk

xxx: rtyuiol_123
ght: ertyjk

xxx: foo
ght: whatever

xxx: bar
ght: whatever

xxx: rtyuiol
ght: YUIOPO

xxx: rtyuiopfghj
ght: YUIOPO

xxx: dfghjkvbnm
ght: YUIOPO

xxx: qzdfghnbvfgh
ght: YUIOPO

xxx: qsxcvghuiokmnhgf
ght: YUIOPO

那么上面的代码将更新为:

awk '
    BEGIN { FS="\n" }
    NR==FNR {
        tgts[$0]
        next
    }
    $2 != prev {
        if ( inTgts = ($2 in tgts) ) {
            print $2
        }
        prev = $2
    }
    inTgts { print $1 }
' targets RS='' file
ght: ertyjk
xxx: rtyuiol
xxx: ertyuikl_fghjk
xxx: qwertyujkl
xxx: rtyuiol_123
ght: YUIOPO
xxx: rtyuiol
xxx: rtyuiopfghj
xxx: dfghjkvbnm
xxx: qzdfghnbvfgh
xxx: qsxcvghuiokmnhgf

相关内容