看着这个问题,我注意到,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 对进程替换的支持(这假设bash
、zsh
或某些实现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