通过管道交互使用 awk

通过管道交互使用 awk

看着这个问题,我注意到,awk如果文件通过管道传输到标准输入,则无法读取用户输入,但如果从作为命令行参数给出的文件读取输入,它的行为确实符合预期。

例如,如果您的awk脚本中有以下 BEGIN 块:

BEGIN {
    printf "Enter input: "
    getline var < "-"
}

如果你像这样运行它awk -f ./script.awk file.txt,它会提示用户输入,然后继续处理file.txt。但是,如果您像 一样运行cat file.txt | awk -f ./script.awk,我想 awk 会将其从管道获取的内容解释为用户的输入(因此getline将填充var的第一行file.txt)。

有没有办法让它awk表现得像从文件中读取,但在管道中使用?

当然,我可以使用临时文件,但这远非优雅。

答案1

进程替换可以在简单的情况下工作,但这是一种通用方法,允许在处理管道数据的同时与控制台用户对话:

BEGIN {
    printf "Enter input: " > "/dev/tty"
    getline var < "/dev/tty"
    print var
}

答案2

除了临时文件之外,您还可以使用 shell 对进程替换的支持(这假设bashzsh或某些实现ksh(该功能由 ksh88 引入)):

awk -f ./script.awk <(cat file.txt)

这将提供awk一个文件名来代替<(...)构造,该构造在读取时将包含所附命令的输出。

答案3

如果这适合您的工作流程,您可以让 awk 本身打开管道。您不能对元素执行此操作ARGV,因此您不会在从管道读取的行上获得 awk 的自动迭代。相反,awk 将从作为命令行参数传递的文件中读取,如果没有文件参数,则从标准输入中读取。

{
    printf "Enter input: "
    getline var
    while (("cat file.txt" | getline) > 0) { … }
}

这种结构不太适合您的玩具示例,但我提到它是因为它可能适合您的实际问题。

答案4

如果您的系统支持/dev/fd/[num]链接或者您awk是 GNU awk,那么这应该可以在以下位置工作awk -f ./script.awk file.txt

{ some_cmd | 3<&0 <&4 4<&- awk -f ./script.awk /dev/fd/3; } 4<&0

相关内容