Awk

從外部文件讀取,匹配後跳過行,列印並退出

  • August 4, 2022

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文件中一次讀取一行,在每次迭代時呼叫 awk,搜尋與key模式匹配的行,如果匹配,則執行一個for循環,跳過 9 行,然後列印最終一。

這是所述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 內部讀取外部文件是最重要的。

非常感謝您提前提供的幫助!

您可以執行以下操作:

#! /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仍然有效(在這種情況下它仍然是有效的程式碼,但什麼都不做)。

請注意,POSIX 沒有指定 shebang 機制,也沒有指定awk實用程序的路徑。#! /path/to/awk -fshebangs 不可靠,因為在呼叫時, athat-script -x變為/path/to/awk -f /path/to/that-script -x,其中-x可以將其視為一個選項awk(並且類似的參數'-eBEGIN{system("reboot")}'會在 GNU 實現中重新啟動awk

In "date..." | getline date,awk確實呼叫sh以呼叫命令行,因此不會sh從等式中刪除。awk沒有sh. GNUawk可以格式化目前日期,但它不是標準的。您可以使用srand()POSIXly 將目前日期作為紀元時間(但 OpenBSD 在這方面不是 POSIX),但是在使用者的時區將其轉換為 YYYy-MM-DD 格式將非常困難。perl如果要避免的話,可能是比awk這裡更好的語言sh

請注意,如果 的行採用awk.data格式foo=bar.htmlawk則將它們視為變數賦值而不是要處理的文件路徑。如果可能是這種情況,您可以使用以下命令在 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

引用自:https://unix.stackexchange.com/questions/712367