bash脚本读取管道或参数

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)


mr.*_*tic 6

如果您只需要知道它是管道还是重定向,那么确定 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 | myscriptmyscript < infile。这是最简单的解决方案,假设您的脚本用于交互式使用。

[命令是 bash 和其他一些 shell 中的内置命令,并且由于[/ testwith-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。但是,这些更适合非阻塞轮询循环,而不是一次性检查:当您在管道中启动两个或多个进程时会出现竞争条件,因为一个进程可能在另一个进程写入之前准备好读取。


gre*_*ers 5

首先检查命令行参数更容易,如果没有参数则回退到标准输入。Shell Parameter Expansion是一个很好的速记,而不是 if-else:

value=${*:-`cat`}
Run Code Online (Sandbox Code Playgroud)