bash 使用 stdin 作为变量

Joe*_*oey 2 unix bash shell

我想确保当用户使用如下语法时我的脚本能够正常工作:

script.sh firstVariable < SecondVariable
Run Code Online (Sandbox Code Playgroud)

由于某种原因我无法让它工作。

我想要 $1=firstVariable 和 $2=SecondVariable

但由于某种原因,我的脚本认为只有firstVariable 存在?

ric*_*ici 5

这是一个经典的XY 问题。目标是编写一个实用程序,其中

utility file1 file2
Run Code Online (Sandbox Code Playgroud)

utility file1 < file2
Run Code Online (Sandbox Code Playgroud)

有相同的行为。似乎很想找到一种方法,通过(以某种方式)找出 stdin 的“名称”,然后以与使用第二个参数相同的方式使用该名称,将第二个调用转换为第一个调用。不幸的是,这是不可能的。重定向发生在调用实用程序之前,并且没有可移植的方法来获取打开的文件描述符的“名称”。(事实上​​,在 的情况下,它甚至可能没有名称other_cmd | utility file1。)

因此,解决方案是关注所要求的内容:使两种行为保持一致。grep大多数标准实用程序( 、cat、等)都是这种情况sort:如果未指定输入文件,则实用程序使用stdin.

在许多 UNIX 实现中,stdin实际上有一个名称:/dev/stdin。在这样的系统中,可以轻松实现上述目标:

utility() {
  utility_implementation "$1" "${2:-/dev/stdin}"
}
Run Code Online (Sandbox Code Playgroud)

实际上在哪里utility_implementation做了需要做的事情。第二个参数的语法是正常的默认参数扩展$2它表示if$2存在且非空的值,否则表示 string /dev/stdin。(如果您省略了,使其成为“${2:/dev/stdin}”,那么如果存在且为空,-它将不会进行替换,这可能会更好。)$2

解决该问题的另一种方法是确保第一个语法与第二个语法相同,以便输入始终来自stdin即使具有命名文件的情况。显而易见的简单方法:

utility() {
  if (( $# < 2 )); then
    utility_implementation "$1"
  else
    utility_implementation "$1" < "$2"
  fi
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用exec仅带有重定向的命令来重定向 shell 自己的stdin. 请注意,我们必须在子 shell 中执行此操作((...)而不是{... }),以便重定向不适用于调用该函数的 shell:

utility() (
  if (( $# > 1 )) then; exec < "$2"; fi
  # implementation goes here. $1 is file1 and stdin
  # is now redirected to $2 if $2 was provided.
  # ...
)
Run Code Online (Sandbox Code Playgroud)