ファイルから渡すときに変数が 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

最初の試みでは、シェル変数の展開に二重引用符を使用し、次に awk 演算子の二重引用符をエスケープして、シェルによって展開されないようにする必要がありますが、このように使用すると、変数に,などの特殊文字が含まれている$場合に awk が機能しなくなることに注意してください。[コマンドに関する他の 1 つ以上の問題を修正するためにスキップします]。$i\/

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

2 回目の試行では、現在の行に対して正規表現一致または文字列一致のいずれかを使用する必要があります。たとえば、次のように正規表現一致 (部分正規表現一致) を使用します。

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 で実行し、シェル ループを使用して終了することができます。ここでは、完全な文字列一致を実行しています。

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 readループは必要ありませんし、shでテキスト処理を行うのは悪い考えです(シェル ループを使用してテキストを処理するのはなぜ悪い習慣だと考えられるのでしょうか?)。

代わりに、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}最初の入力ファイルを 2 番目以降の入力ファイルとは異なる方法で処理するための非常に一般的な 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

テキストを操作するためにシェルループを使用しないでください。シェル ループを使用してテキストを処理するのはなぜ悪い習慣だと考えられるのでしょうか?シェルを発明した人々は、シェルがテキストを操作するために呼び出す awk も発明しました。

あらゆる Unix ボックス上のあらゆるシェルで 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#複数行RS を null に設定すると複数行のレコードを処理できるようになり、FS を改行に設定すると、そのようなレコードの各フィールドが 1 行全体になるため、データは空白行で区切られたレコードとして処理され、各レコードには 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

関連情報