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文件中读取一行,在每次迭代时调用 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 -fshebangs 并不可靠,因为在调用时, athat-script -x会变成/path/to/awk -f /path/to/that-script -x,其中-x可以被视为一个选项awk( 并且像这样的参数'-eBEGIN{system("reboot")}'会通过 GNU 实现重新启动,awk例如。

在 中"date..." | getline dateawk确实调用sh来调用命令行,因此不会sh从等式中删除。awk没有 的帮助就无法运行命令sh。 GNUawk可以格式化当前日期,但这不是标准的。您可以使用 POSIXly 获取当前日期作为纪元时间srand()(但 OpenBSD 在这方面不是 POSIX),但随后将其转换为用户时区的 YYYy-MM-DD 格式将非常困难。如果要避免的话,perl可能会是一种比这里更好的语言。awksh

请注意,如果 的行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

相关内容