从 GNU awk 协进程非阻塞读取?

Non blocking read from GNU awk coprocess?

我想使用 gawk 实现脚本的增量执行,以便在文档中交错脚本源和脚本输出。

我们的想法是将脚本行读入 awk 以打印它们,并将它们通过管道传输到适当的解释器中。然后,在输入文件的队列中,读取协处理的任何输出并将其打印到标准输出。不过好像在循环coprocess输出之前必须要知道产生了多少输出。

有什么方法可以从协进程进行非阻塞读取吗?

function script_checkpoint() {
    while(("python3" |& getline output) > 0)
        print output
}

/^# checkpoint/ { script_checkpoint(); next }
{ print; print [=10=] |& "python3" }
END { script_checkpoint() }

编辑:我试图在不使用协进程的情况下实现这一点,方法是将输入行缓冲到检查点,然后让解释器打印到标准输出本身,但解释器始终缓冲其输出,直到流关闭。在程序结束以保留其内部状态之前,我不想关闭它。

编辑:更清楚地表明我的第一个预期用例是 运行 python 脚本。这是一个示例 input/output 对。

print('first line')

# checkpoint

print('second line')

应该导致

print('first line')

first line

print('second line')

second line

听起来你正在尝试做这样的事情:

BEGIN { cmd="/my/python/script/path" }

function script_checkpoint(      output) {
    close(cmd,"to")
    while ( (cmd |& getline output) > 0 ) {
        print output
    }
    close(cmd)
}

/^# checkpoint/ {
    script_checkpoint()
    next
}
{
    print
    print |& cmd
}

END { script_checkpoint() }

一般问题:

  • while ((interpreter |& getline output) > 0) 运行s 直到它看到 EOF 但是...
  • interpreter 不会 end/terminate/exit,因此不会发送 EOF 所以 ...
  • awk 在等待 interpreter 发送更多数据时挂起,因此 ...
  • 我们最终遇到了死锁情况(awk 等待来自 interpreter 的输入;interpreter 等待来自 awk 的输入)

假设:

  • 需要在整个 运行 期间保持对 interpreter 的一次调用(根据 OP 的评论);最终结果:awk 不能依赖于 interpreter 发送 EOF
  • interpreter可以修改(生成额外的输出)
  • awk 脚本无法知道 interpreter
  • 将生成多少行输出

一个想法是在 awkinterpreter 之间设置握手。在 while ((interpreter |& getline output) > 0) 循环中,我们将测试我们的握手,当我们看到它跳出循环并 return 回到主 awk 脚本时。

出于演示目的,我将使用一个简单的 bash 脚本来执行一些握手处理,否则只是将它从标准输入读取的内容打印到标准输出:

$ cat interpreter
#!/usr/bin/bash

while read -r line
do
    if [[ "${line}" = 'checkpoint' ]]              # received 'checkpoint' handshake?
    then
        echo "CHECKPOINT"                          # send "CHECKPOINT" handshake/acknowledgement
        continue
    else
        echo "interpreter: $line"
    fi
done

演示 awk 带有握手逻辑的代码:

awk '
function script_checkpoint() {
    while (( cmd |& getline output) > 0) {
          if ( output == "CHECKPOINT" )            # received "CHECKPOINT" handshake/acknowledgement?
             break
          print output
    }
}

BEGIN           { cmd= "./interpreter" }

/^# checkpoint/ { print "checkpoint" |& cmd        # send "checkpoint" handshake
                  script_checkpoint()
                  next
                }

                { print "awk: " [=11=]
                  print [=11=] |& cmd
                }

END             { print "awk: last checkpoint"     # in case last line of input is not "# checkpoint" we will ...
                  print "checkpoint" |& cmd        # send one last "checkpoint" handshake
                  script_checkpoint()
                  print "awk: done"
                }
' test.dat

示例输入文件:

$ cat test.dat
line1
line2
# checkpoint
line3
line4
# checkpoint
line5

输出:

awk: line1
awk: line2
interpreter: line1
interpreter: line2
awk: line3
awk: line4
interpreter: line3
interpreter: line4
awk: line5
awk: last checkpoint
interpreter: line5
awk: done

备注:

  • awk 仍然会在 interpreter 崩溃的情况下挂起 and/or 无法发回 CHECKPOINT 握手
  • 如果字符串 checkpoint and/or CHECKPOINT 可以出现在 'normal' 数据流中,则更新代码以使用数据流中不期望的字符串