【问题标题】:bash script read pipe or argumentbash 脚本读取管道或参数
【发布时间】:2013-12-03 12:30:02
【问题描述】:

我希望我的脚本从 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

问题是当它没有通过管道传输时,脚本将停止并等待“ctrl d”,我不希望这样。有关如何解决此问题的任何建议?

提前致谢。 /托马斯

【问题讨论】:

  • 你总是只能从标准输入读取。如果您想通过命令行传递输入,那么您可以使用your_script <<< 'some string'——您的脚本会将其视为来自标准输入的普通输入。

标签: linux bash pipe stdin


【解决方案1】:

先检查参数怎么样?

if (($#)) ; then
     process "$1"
else
     cat | process
fi

或者,只是利用cat 的相同行为:

cat "$@" | process

【讨论】:

  • 无法获得我想要的工作。在鱼中使用test 或不使用if 或其他命令来获取参数列表计数会窃取潜在的管道输入,echo $argv | cat | read ... 也是如此。如果我真的想要这个功能,也许是 ruby​​ 的 ARGF 甚至更大那时可能是编程语言,但我不需要它。我想我现在只是将所有内容作为提示或论据强行通过。
【解决方案2】:

如果您只需要知道它是管道还是重定向,那么确定标准输入是否是终端就足够了:

if [ -t 0 ]; then
    # stdin is a tty: process command line
else 
    # stdin is not a tty: process standard input
fi

[ (aka test) 和 -t 等效于 libc isatty() 函数。 以上内容适用于something | myscriptmyscript < infile。这是最简单的解决方案,假设您的脚本用于交互式使用。

[ 命令是 bash 和其他一些 shell 中的内置命令,并且由于 [/test-tis in POSIX,它也是可移植的(不依赖于 Linux、bash 或 GNU 实用程序功能)。

有一种极端情况,如果文件描述符无效,test -t 也会返回 false,但要安排这一点需要一些轻微的逆境。 test -e 会检测到这一点,但假设您有一个文件名,例如 /dev/stdin 可以使用。

也可以使用 POSIX tty 命令,并处理上述逆境。如果 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

(使用 GNU tty,您可以使用 tty -s 进行静默操作)

虽然在典型的 Linux 上肯定可以接受,但可移植性较差的方法是使用 GNU stat 及其 %F 格式说明符,这将返回文本“字符特殊文件”、“fifo”和“常规文件”分别是终端、管道和重定向的情况。 stat 需要文件名,因此您必须提供/dev/stdin/dev/fd/0/proc/self/fd/0 形式的特殊命名文件,并使用-L 来跟踪符号链接:

stat -L -c "%F" /dev/stdin

这可能是处理非交互式使用的最佳方式(因为那时您无法对终端做出假设),或者检测不同于重定向的实际管道 (FIFO)。

%F 有一个小问题,因为您无法使用它来区分 终端 和某些其他设备文件,例如 /dev/zero/dev/null,它们也是“字符特殊文件”并且可能合理地出现。一个不太好的解决方案是使用%t 报告底层设备类型(主要,以十六进制表示),假设您知道底层 tty 设备编号范围是什么......这取决于您使用的是 BSD 样式 ptys 还是 Unix98 ptys,或者您是否在实际控制台上,等等。在简单的情况下,%t 将是 0,但对于管道或普通(非特殊)文件的重定向。

此类问题的更通用解决方案是使用 bash 的 read 超时 (read -t 0 ...) 或非阻塞 I/O 与 GNU dd (dd iflag=nonblock)。

后者将允许您检测标准输入是否缺少输入,dd 如果没有准备好读取的内容,将返回退出代码 1。但是,这些更适合非阻塞轮询循环,而不是一次性检查:当您在管道中启动两个或多个进程时存在竞争条件,因为一个进程可能在另一个进程写入之前准备好读取。

【讨论】:

    【解决方案3】:

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

    value=${*:-`cat`}
    # do something with $value
    

    【讨论】:

    • 您愿意提供一个更完整的示例吗?这看起来是一个有趣的想法,但很难理解如何使用它来解决问题。
    • 这个问题没有对输入做任何事情,所以没有什么要补充的。它创建了一个变量,该变量具有所有参数作为它的值。如果没有提供参数,它将回退到标准输入。所以只需使用$value 作为常规变量。
    猜你喜欢
    • 2013-01-26
    • 2014-07-20
    • 1970-01-01
    • 1970-01-01
    • 2016-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多