從外部文件讀取,匹配後跳過行,列印並退出
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 -f
shebangs 不可靠,因為在呼叫時, 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.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