在Go中,是否可以通过简单的方式从stdin中读取单行输入,同时满足以下要求?
- 可以被更大的交互式应用程序的不同部分调用,而不必在应用程序的这些不同部分之间创建耦合(例如,通过在它们之间传递全局
bufio.Scanner
) - 无论用户是在运行交互式终端还是使用预先编写的输入脚本均有效
我想修改一个现有的大型 Go 应用程序,该应用程序当前每次要求用户输入一行时都会创建一个 bufio.Scanner
实例。当标准输入来自终端时,多个实例工作正常,但当标准输入从另一个进程通过管道传输时,对 Scan
的调用仅在 bufio.Scanner
的第一个实例上成功。来自所有其他实例的调用失败。
这是演示问题的一些玩具代码:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// read with 1st scanner -> works for both piped stdin and terminal
scanner1 := readStdinLine(1)
// read with 2nd scanner -> fails for piped stdin, works for terminal
readStdinLine(2)
// read with 1st scanner -> prints line 2 for piped stdin, line 3 for terminal
readLine(scanner1, 3)
}
func readStdinLine(lineNum int64) (scanner *bufio.Scanner) {
scanner = readLine(bufio.NewScanner(os.Stdin), lineNum)
return
}
func readLine(scannerIn *bufio.Scanner, lineNum int64) (scanner *bufio.Scanner) {
scanner = scannerIn
scanned := scanner.Scan()
fmt.Printf("%d: ", lineNum)
if scanned {
fmt.Printf("Text=%s\n", scanner.Text())
return
}
if scanErr := scanner.Err(); scanErr != nil {
fmt.Printf("Error=%s\n", scanErr)
return
}
fmt.Println("EOF")
return
}
我将其构建为 print_stdin
并从 bash shell 交互式运行:
~$ ./print_stdin
ab
1: Text=ab
cd
2: Text=cd
ef
3: Text=ef
但是如果我输入文本,第二个 bufio.Scanner
会失败:
~$ echo "ab
> cd
> ef" | ./print_stdin
1: Text=ab
2: EOF
3: Text=cd
最佳答案
你的顺序是:
- 创建扫描器
- 等待读取终端
- 打印结果
- 重复 1 到 3(创建关于 stdin 的新扫描器)
- 重复 2 到 3
- 退出程序
当你在管道中执行 echo 时,只存在一个正在读/写的标准输入/标准输出文件,但你试图使用两个。
更新: echo 的执行流程是:
- 阅读参数
- 处理参数
- 在标准输出中写入参数
- 终端读取标准输出并打印它
看到这发生在按 ENTER 键时。整个参数被发送到 echo 程序,而不是按行发送。
The echo utility writes its arguments to standard output, followed by a . If there are no arguments, only the is written.
更多信息:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html .
参见source code回声如何工作:
while (argc > 0)
{
fputs (argv[0], stdout);//<-- send args to the same stdout
argc--;
argv++;
if (argc > 0)
putchar (' ');
}
所以你的代码可以正常工作:
$ (n=1; while sleep 1; do echo a$n; n=$((n+1)); done) | ./print_stdin
$ 1: Text=a1
$ 2: Text=a2
$ 3: Text=a3
如果您需要在不同的标准输出中重复参数,请使用"is"程序或替代方案。 是 程序在标准输出中重复写入的参数。更多内容: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/yes.c
例子:
$ yes a | ./print_stdin
$ 1: Text=a
$ 2: Text=a
$ 3: Text=a
关于go - 如何在没有共享 bufio.Scanner 的情况下重复读取 os.Stdin,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50436786/