ファイルを行ごとに読み取っています。各行は次のようになります。
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