当没有传递参数时如何读取标准输入?

17

当我想使用标准输入时,脚本在没有传递参数(文件)时无法工作。是否有任何方法可以在此代码中使用stdin而不是文件?

我尝试过这个:

if [ ! -n $1 ] # check if argument exists
   then
   $1=$(</dev/stdin)  # if not use stdin as an argument
   fi

var="$1"
while read line
   do
   ...                # find the longest line
   done <"$var"

3
可能是从文件或标准输入读取Bash的重复问题。 - Oswald
5个回答

17

如果需要从标准输入读取一个值,但参数缺失的情况下,这个方法可行。

$ echo param | script.sh
$ script.sh param

脚本.sh

#!/bin/bash

set -- "${1:-$(</dev/stdin)}" "${@:2}"

echo $1

我更喜欢这个解决方案而不是循环,因为你不必将逻辑嵌入到任何外部代码中。 - Sridhar Sarnobat
3
如果您想从位置参数中读取 所有 参数,请使用 set -- ${@:-$(</dev/stdin)},否则请使用标准输入。 (就像 grep 的行为一样。) - bishop
可以!您能解释一下 "${@:2}" 是什么吗? - Marinos An
从其他答案中窃取 - 它会扩展到剩余的参数 - Ryan

12

5

pilcrow's answer 提供了一种优雅的解决方案;这是对为什么OP的方法不起作用的解释

OP的方法的主要问题是尝试使用$1=...将值赋给位置参数$1,但这并不起作用。

Shell会将LHS扩展为$1,并将结果解释为要分配的变量的名称 - 显然,这不是意图。

在bash中分配$1的唯一方法是通过set内置命令。但有一个警告:无论如何,set都会设置所有位置参数,因此如果有其他参数,您也必须包括它们。

set -- "${1:-/dev/stdin}" "${@:2}"     # "${@:2}" expands to all remaining parameters

如果你只期望最多一个参数,set -- "${1:-/dev/stdin}" 将会起作用。
上述方法还纠正了 OP 的方法中的一个次要问题:尝试存储标准输入流的内容而不是文件名到 $1 中,因为使用了 <${1:-/dev/stdin} 是 bash 参数扩展 的应用,它表示:返回 $1 的值,除非 $1 未定义(没有传递参数)或其值为空字符串(传递了 ""'')。变量 ${1-/dev/stdin}(没有 :)只有在 $1 未定义时才会返回 /dev/stdin(如果它包含任何值,即使是空字符串,也会被返回)。
将所有内容组合起来:
# Default to filename '/dev/stdin' (stdin), if none was specified.
set -- "${1:-/dev/stdin}" "${@:2}"

while read -r line; do
   ...                # find the longest line
done < "$1"

当然,更简单的方法是直接使用${1:-/dev/stdin}作为文件名:

while read -r line; do
   ...                # find the longest line
done < "${1:-/dev/stdin}"

或者,通过一个中间变量:
filename=${1:-/dev/stdin}
while read -r line; do
   ...                # find the longest line
done < "$filename"

1

变量通过Var=Value赋值,然后可以使用该变量,例如echo $Var。在您的情况下,这将等同于

1=$(</dev/stdin)

在分配标准输入时,变量名称不允许以数字字符开头。请参阅问题bash read from file or stdin以了解解决方法。

事实上,在bash中,1不是一个有效的变量名。除此之外,给位置参数 $1$2等赋值的唯一方法是使用内置命令 setset -- <valueFor$1> <valueFor$2> ...)。 - mklement0

1
这是我的脚本版本:
#!/bin/bash
file=${1--} # POSIX-compliant; ${1:--} can be used either.
while IFS= read -r line; do
  printf '%s\n' "$line"
done < <(cat -- "$file")

如果参数中不存在文件,则从标准输入读取。
查看更多示例:如何在bash中从文件或stdin读取? 在stackoverflow SE上。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接