POSIX awkウィザード、あなたの助けが必要です!一見すると、この質問は些細なように思えるかもしれませんが、私の意図をもう少し詳しく説明します。
私はスタンドアロンPOSIX awkプログラムを開発しており、95%完了しましたが、正しい方法が見つかりませんでした。これについては後でお見せします。
POSIX shのソリューション
まず、実装したいPOSIX shソリューションは次のとおりです。
#!/bin/sh
key=$(date +%Y-%m-%d) # results in 2022-08-04
while read -r line; do
awk -v key=$key '$0 ~ key {
for (i = 0; i < 10; i++)
getline current
print current
}' "$line"
done < /tmp/awk.data
上記のコードスニペットからわかるように、
awk.data
ファイルから一度に1行ずつ読み込み、繰り返すたびにawkを呼び出し、パターンに一致する行を検索し、key
一致するとループを実行しfor
、9行をスキップし、印刷します。最終結果 1.
ファイルの内容は次のとおりですawk.data
。
$ cat /tmp/awk.data
/tmp/sample-001.html
/tmp/sample-002.html
/tmp/sample-003.html
# <...>
/var/log/sample-787.html
/var/log/sample-788.html
POSIX awkの問題を解決しようとしています。
これはPOSIX awkプログラムで実装したいもののほんの一部であり、これまでに試したことは次のとおりです。しかし、成功しませんでした。
#!/usr/bin/awk -f
BEGIN {
date = getdate()
data = "/tmp/awk.data"
# <...>
read(data)
}
function getdate() {
cmd = "date +%Y-%m-%d"
cmd | getline date
close(cmd)
return date
}
function read(data) {
cmd = "cat" " " data
while (cmd | getline line)
parse(line)
close(cmd)
}
function parse(file) {
cmd = "cat" " " file
while (cmd | getline line) {
if (line ~ date) {
for (i = 0; i < 10; i++)
getline current
print current
}
}
close(cmd)
}
このread
関数は、出力の各行cat
(たとえば、
/tmp/sample-001.html
等)を読み取り、それを各ファイルを解析し、目的の出力を生成する他の関数/tmp/sample-002.html
に渡します。parse
while
これは、処理された各行にループを使用し、現在の行が変数で定義されたパターンと一致することを確認する最初の試みですdate
。その場合はfor
、ループを開始して9行をスキップして最後の行を印刷します。それは非常に可能です非常に
非効率的ですが、プログラムは実行されますが、永久に繰り返され、何も印刷されません。完全に閉じ込められました!
つまり、私のawkプログラムはどのパラメータも受け入れません。したがって、この場合、awkの内部から外部ファイルを読み取ることが重要です。
事前に助けてくれてありがとう!
ベストアンサー1
次のことができます。
#! /usr/bin/awk -f
BEGIN {
ARGC = 1
while ((getline file < "awk.data") > 0)
ARGV[ARGC++] = file
"date +%Y-%m-%d" | getline date
}
FNR == 1 {
line_to_print = 0
}
line_to_print {
if (FNR == line_to_print) {print; nextfile}
next
}
index($0, date) {line_to_print = FNR + 10}
nextfile
まだPOSIXではありませんが、次のバージョンに含まれる予定です。上記のコードはawk
サポートされていない実装でも動作しますnextfile
(この場合はまだ有効なコードですが、何もしません)。
awk
POSIXはShebangメカニズムを指定せず、ユーティリティパスも指定しません。 shebangsは、呼び出されたときにaをオプションとして扱うことができる#! /path/to/awk -f
ので信頼できません(たとえば、このような引数はGNU実装によって再開されます)。that-script -x
/path/to/awk -f /path/to/that-script -x
-x
awk
'-eBEGIN{system("reboot")}'
awk
inはコマンドラインを呼び出すために呼び出される"date..." | getline date
ので、式からコマンドラインは削除されません。ヘルプなしではコマンドを実行できません。 GNU は現在の日付の書式を指定できますが、これは標準ではありません。 POSIXlyを使用して現在の日付を元の時刻にインポートできますが(しかしOpenBSDはこの点ではPOSIXではありません)、ユーザーのタイムゾーンに合わせてYYYy-MM-DD形式に変換するのは非常に困難です。それを避ければ、おそらくここより良い言語があるでしょう。awk
sh
sh
awk
sh
awk
srand()
perl
awk
sh
awk.data
行が次のfoo=bar.html
形式の場合、awk
処理するファイルパスではなく変数の割り当てとして扱われます。この場合、次を使用してBEGINステートメントでこれらのパスをクリーンアップできます。
function sanitise(path) {
if (path != "" && path !~ /^\//)
return "./" path
else
return path
}
(ARGV[ARGC++] = sanitise(file)
代わりに使用してくださいARGV[ARGC++] = file
)。
一方getline file
、read -r line
先頭と末尾のスペースとタブ文字は入力行から削除されません。削除するには手動で行う必要があります。
getline file
sub(/^[ \t]*/, "", file)
sub(/[ \t]*$/, "", file)
例えば。
ループとの別の違いは、while read
最後の行が区別されていない場合はまだ処理されますが、awk
ループによって削除されることですwhile read
sh
。