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 内部读取外部文件至关重要。
非常感谢您提前的帮助!
答案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
(在这种情况下它仍然是有效的代码,但什么也不做)。
请注意,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
例如。
在 中"date..." | getline date
,awk
确实调用sh
来调用命令行,因此不会sh
从等式中删除。awk
没有 的帮助就无法运行命令sh
。 GNUawk
可以格式化当前日期,但这不是标准的。您可以使用 POSIXly 获取当前日期作为纪元时间srand()
(但 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
。