Tom*_*Tom 6 linux bash stdin pipe
我希望我的脚本从stdin读取字符串,如果是管道,或者从参数读取.所以首先我想检查一些文本是否是管道,如果不是,它应该使用一个参数作为输入.我的代码看起来像这样:
value=$(cat) # read from stdin
if [ "$pipe" != "" ]; then #check if pipe is not empty
#Do something with pipe string
else
#Do something with argument string
fi
Run Code Online (Sandbox Code Playgroud)
问题是当它没有管道时,脚本将停止并等待"ctrl d",我不想要那个.关于如何解决这个问题的任何建议?
提前致谢./托马斯
cho*_*oba 10
先检查参数怎么样?
if (($#)) ; then
process "$1"
else
cat | process
fi
Run Code Online (Sandbox Code Playgroud)
或者,只是利用相同的行为cat
:
cat "$@" | process
Run Code Online (Sandbox Code Playgroud)
如果您只需要知道它是管道还是重定向,那么确定 stdin 是否是终端就足够了:
if [ -t 0 ]; then
# stdin is a tty: process command line
else
# stdin is not a tty: process standard input
fi
Run Code Online (Sandbox Code Playgroud)
[
(aka test
) with-t
等价于 libcisatty()
函数。以上将适用于something | myscript
和myscript < infile
。这是最简单的解决方案,假设您的脚本用于交互式使用。
该[
命令是 bash 和其他一些 shell 中的内置命令,并且由于[
/ test
with-t
在 POSIX 中,因此它也是可移植的(不依赖于 Linux、bash 或 GNU 实用程序功能)。
有一种边缘情况,test -t
如果文件描述符无效,也会返回 false,但安排它需要一些轻微的逆境。test -e
将检测到这一点,尽管假设您有一个文件名/dev/stdin
可以使用。
tty
也可以使用POSIX命令,处理上面的逆境。如果 stdin 是终端,它将打印 tty 设备名称并返回 0,并且在任何其他情况下将打印“not a tty”并返回 1:
if tty >/dev/null ; then
# stdin is a tty: process command line
else
# stdin is not a tty: process standard input
fi
Run Code Online (Sandbox Code Playgroud)
(使用 GNU tty
,您可以tty -s
用于静默操作)
一种不太便携的方式,虽然在典型的 Linux 上当然可以接受,但使用 GNUstat
及其%F
格式说明符,这在终端、管道和重定向的情况下分别返回文本“字符特殊文件”、“fifo”和“常规文件” . stat
需要一个文件名,所以你必须提供一个特殊命名的文件,格式为/dev/stdin
、/dev/fd/0
、 或/proc/self/fd/0
,并用于-L
追踪符号链接:
stat -L -c "%F" /dev/stdin
Run Code Online (Sandbox Code Playgroud)
这可能是处理非交互式使用的最佳方式(因为那时您无法对终端做出假设),或者检测与重定向不同的实际管道 (FIFO)。
有一个小问题,%F
因为您无法使用它来区分终端和某些其他设备文件之间的区别,例如,/dev/zero
或者/dev/null
它们也是“字符特殊文件”并且可能会合理出现。一个不漂亮的解决方案是使用%t
报告底层设备类型(主要,十六进制),假设您知道底层 tty 设备编号范围是什么......这取决于您使用的是 BSD 风格的 ptys 还是 Unix98 ptys,或者您是否在实际的控制台上,等等。在简单的情况下,%t
对于管道或正常(非特殊)文件的重定向,将为 0。
此类问题的更通用解决方案是将 bashread
与超时 ( read -t 0 ...
) 或非阻塞 I/O 与 GNU dd
( dd iflag=nonblock
) 一起使用。
后者将允许您检测 stdin 上缺少输入,dd
如果没有准备好读取的内容,将返回退出代码 1。但是,这些更适合非阻塞轮询循环,而不是一次性检查:当您在管道中启动两个或多个进程时会出现竞争条件,因为一个进程可能在另一个进程写入之前准备好读取。
首先检查命令行参数更容易,如果没有参数则回退到标准输入。Shell Parameter Expansion是一个很好的速记,而不是 if-else:
value=${*:-`cat`}
Run Code Online (Sandbox Code Playgroud)