【问题标题】:bash using stdin as a variablebash 使用标准输入作为变量
【发布时间】:2016-01-05 11:40:59
【问题描述】:

我想确保当用户使用这样的语法时我的脚本可以正常工作:

script.sh firstVariable < SecondVariable

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

我想要 $1=firstVariable $2=SecondVariable

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

【问题讨论】:

  • &lt; 是重定向运算符。它将脚本的stdin 连接到文件SecondVariable
  • 为什么是&lt;?在命令行上传递给可执行文件时,参数用空格分隔。
  • 那么如何让 SecondVariable 的标准输入等于 $2?
  • @marekful,空间是允许的。我不认为它是好的形式,但它是完全有效的语法。
  • @Joey,“SecondVariable 的标准输入等于 $2”是什么意思? “SecondVariable”在这个命令行的上下文中是一个文件名;您当然可以通过读取脚本的标准输入来访问文件的内容。

标签: bash shell unix


【解决方案1】:

这是经典的X-Y problem。目标是编写一个实用程序,其中

utility file1 file2

utility file1 < file2

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

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

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

utility() {
  utility_implementation "$1" "${2:-/dev/stdin}"
}

utility_implementation 实际上做了任何需要做的事情。第二个参数的语法是普通默认parameter expansion;如果$2 存在且非空,则它表示$2 的值,否则为字符串/dev/stdin。 (如果您省略 - 使其为“${2:/dev/stdin}”,那么如果 $2 存在且为空,则它不会进行替换,这可能会更好。)

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

utility() {
  if (( $# < 2 )); then
    utility_implementation "$1"
  else
    utility_implementation "$1" < "$2"
  fi
}

另一种方法是使用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.
  # ...
)

【讨论】:

    【解决方案2】:

    要使第二个变量的标准输入成为脚本的最后一个参数(所以如果你有一个 arg 然后

    #!/bin/bash
    
    ##read loop to read in stdin
    while read -r line
    do
    
      ## This just checks if the variable is empty, so a newline isn't appended on the front
      [[ -z $Vars ]] && Vars="$line" && continue
    
      ## Appends every line read to variable
      Vars="$Vars"$'\n'"$line"
    
      ## While read loop using stdin
    done < /dev/stdin
    
     ##Set re-sets the arguments to the script to the original arguments and then the new argument we derived from stdin
    set - "$@" "$Vars"
    
    ## Echo the new arguments
    echo "$@"
    

    【讨论】:

    • 哇,这很棒,但echo "$@" 当然会回显 $2 的每一行,而不仅仅是文件名(它现在有点像 cat)
    • @joey 如果您希望它只是作为文件名读取,为什么要使用标准输入?
    • @Joey 你不能(你可以,但我不认为是可移植的)得到附加到标准输入的 name。这样做也没有任何实际用处。您认为需要这样做的实际问题是什么?脚本需要做什么?
    • 顺便说一句,你希望&lt; /dev/stdin 做什么?默认情况下还不是这样?
    • @CharlesDuffy:尽管cat 不是内置的,但也可能会想写lines=$(cat)。或lines=$(&lt;/dev/stdin).
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-28
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    • 2021-11-11
    • 2014-01-01
    相关资源
    最近更新 更多