POSIX shのソリューション

POSIX shのソリューション

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(この場合はまだ有効なコードですが、何もしません)。

awkPOSIXはShebangメカニズムを指定せず、ユーティリティパスも指定しません。 shebangsは、呼び出されたときにaをオプションとして扱うことができる#! /path/to/awk -fので信頼できません(たとえば、このような引数はGNU実装によって再開されます)。that-script -x/path/to/awk -f /path/to/that-script -x-xawk'-eBEGIN{system("reboot")}'awk

inはコマンドラインを呼び出すために呼び出される"date..." | getline dateので、式からコマンドラインは削除されません。ヘルプなしではコマンドを実行できません。 GNU は現在の日付の書式を指定できますが、これは標準ではありません。 POSIXlyを使用して現在の日付を元の時刻にインポートできますが(しかしOpenBSDはこの点ではPOSIXではありません)、ユーザーのタイムゾーンに合わせてYYYy-MM-DD形式に変換するのは非常に困難です。それを避ければ、おそらくここより良い言語があるでしょう。awkshshawkshawksrand()perlawksh

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 fileread -r line先頭と末尾のスペースとタブ文字は入力行から削除されません。削除するには手動で行う必要があります。

getline file
sub(/^[ \t]*/, "", file)
sub(/[ \t]*$/, "", file)

例えば。

ループとの別の違いは、while read最後の行が区別されていない場合はまだ処理されますが、awkループによって削除されることですwhile read sh

おすすめ記事