從文件傳遞時變數在 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

相關內容