【问题标题】:How can pass the value of a variable to the standard input of a command?如何将变量的值传递给命令的标准输入?
【发布时间】:2011-06-14 02:48:23
【问题描述】:

我正在编写一个应该有点安全的 shell 脚本,即不通过命令参数传递安全数据,最好不使用临时文件。如何将变量传递给命令的标准输入?

或者,如果不可能,我怎样才能正确地使用临时文件来完成这样的任务?

【问题讨论】:

  • 攻击者可以更改$PATH 吗?这样cat就可以替换成/bin/cat "$@" | tee /attacker/can/read/this/file
  • 如果你是从重复而来的,你可能在你想要的variable=$("$something" | command) 尝试了variable=$(echo "$something" | command)

标签: security bash stdin


【解决方案1】:

如果你是从重复而来的,你可能是一个尝试做类似事情的初学者

"$variable" >file

"$variable" | wc -l

显然你的意思是这样的

echo "$variable" >file
echo "$variable" | wc -l

(真正的初学者也会忘记引号;usually use quotes 除非您有特定的理由忽略它们,至少在您理解引用之前。)

【讨论】:

  • 切线地,计算一个字符串中有多少行通常也是一种反模式;见useless use of wc.
【解决方案2】:

我喜欢Martin's answer,但它存在一些问题,具体取决于变量中的内容。这个

your-command <<< """$your_variable"""

如果变量包含"! 会更好。

【讨论】:

  • 但是为什么呢?此外,我无法重现任何问题:foo1=-; foo2='"'; foo3=\!; cat&lt;&lt;&lt;"$foo1"; cat&lt;&lt;&lt;"$foo2"; cat&lt;&lt;&lt;"$foo3" 对我来说很好。这三个"到底是做什么的? AFAIK 你只是在前面加上一个空字符串。
  • 尝试一些真实的东西。就像你的命令是 ssh somehost。你的变量是一个shell脚本。
  • 这似乎追加了一个换行符
  • """foo""" 被 bash 解析器以与 "foo" 相同的方式完全处理。 "" 只是一个空引号对——开始和结束一个带引号的字符串,其中没有任何内容;所以你有 "$your_variable" 与前面和结尾的空引号字符串连接。
  • 当然,!(即使是双引号)通常会在历史扩展打开的交互式 shell 中引起很多问题,但这是关闭历史扩展的一个很好的理由关闭,因此交互式 shell 解析代码的方式与运行脚本的方式相同。
【解决方案3】:

根据Martin's answer,有一个名为 Here Strings 的 Bash 功能(它本身是更广泛支持的 Here Documents 功能的变体): p>

3.6.7 这里的字符串

here文档的一种变体,格式为:

<<< word

这个词被扩展并提供给它的标准命令 输入。

请注意,Here Strings 似乎仅适用于 Bash,因此,为了提高可移植性,您可能最好使用原始 Here Documents 功能,根据PoltoS's answer

( cat <<EOF
$variable
EOF
) | cmd

或者,上面的一个更简单的变体:

(cmd <<EOF
$variable
EOF
)

您可以省略 (),除非您希望将其进一步重定向到其他命令中。

【讨论】:

    【解决方案4】:

    在 Bash 中将值传递给标准输入很简单:

    your-command <<< "$your_variable"
    

    始终确保在变量表达式周围加上引号!

    请注意,这可能仅适用于bash,不适用于sh

    【讨论】:

    • Herestrings &lt;&lt;&lt; 不保证可用,据我所知,它们不在 POSIX 基础中。只要您只在bash 中运行它们,它们就会按预期工作。我只提到它,因为他们 OP 说的是“shell”而不是专门的“bash”。虽然他确实用bash 标记了这个问题......所以这显然仍然是一个可以接受的答案。
    • @StevenLu printf '%s\n' "$var" 产生与 echo "$var" 相同的结果,但不会中断,例如 var=-en,甚至不需要 bash。
    • 还要注意这里的字符串会附加一个换行符。
    • 我发现这种语法的一个缺点是从第一个命令到其他命令的管道输出不像echo "$your_variable" | command1 | command2 那样直观。在这种情况下,这里的字符串的语法是什么?
    • @chunk_split 如果您愿意,可以将此处的字符串放在命令的开头:&lt;&lt;&lt; "$your_variable" command1 | command2
    【解决方案5】:

    简单但容易出错:使用echo

    这样简单的事情就可以解决问题:

    echo "$blah" | my_cmd
    

    请注意,如果$blah 包含-n-e-E 等,这可能无法正常工作;或者如果它包含反斜杠(默认情况下,echo 的 bash 副本在没有 -e 的情况下保留文字反斜杠,但如果启用了可选的 XSI 扩展,即使没有 -e,也会将它们视为转义序列并用相应的字符替换它们)。

    更复杂的方法:使用printf

    printf '%s\n' "$blah" | my_cmd
    

    这没有上面列出的缺点:所有可能的 C 字符串(不包含 NUL 的字符串)都被原封不动地打印出来。

    【讨论】:

    • 也许您可以在此答案中添加printenv blah | my_cmd
    • @mallwright printenv 仅适用于导出的变量。
    【解决方案6】:

    这种健壮且可移植的方式已经出现在 cmets 中。它应该是一个独立的答案。

    printf '%s' "$var" | my_cmd
    

    printf '%s\n' "$var" | my_cmd
    

    注意事项:

    • echo好,原因在这里:Why is printf better than echo?
    • printf "$var" 是错误的。第一个参数是格式,其中各种序列(如%s\n)被解释。要正确传递变量,不得将其解释为格式。
    • 通常变量不包含尾随换行符。前一个命令(带有%s)按原样传递变量。但是,处理文本的工具可能会忽略或抱怨不完整的行(请参阅Why should text files end with a newline?)。因此,您可能需要后一个命令(使用%s\n)将换行符附加到变量的内容。非显而易见的事实:

      • Bash 中的字符串 (&lt;&lt;&lt;"$var" my_cmd) 确实附加了一个换行符。
      • 任何附加换行符的方法都会导致非空标准输入my_cmd,即使变量为空或未定义。

    【讨论】:

      【解决方案7】:
      (cat <<END
      $passwd
      END
      ) | command
      

      cat 并不是真正需要的,但它有助于更​​好地构建代码并允许您使用括号中的更多命令作为命令的输入。

      【讨论】:

      • 但是这种方法允许您将多行传递给您的命令,并且还允许$passwd中的空格
      • 这是迄今为止最好的答案,它不会将变量内容泄漏到管道或进程监听。
      【解决方案8】:

      试试这个:

      echo "$variable" | command
      

      【讨论】:

      • 但内容 $variable 不会显示在例如echo 运行时ps -u 的输出?
      • 不,不会的。 echo是内置的,所以ps中没有显示进程
      • 注意变量中的空格:echo "$variable" 更好。
      • 没错,bash 会吃掉双空格,而 smarter shell 不会
      【解决方案9】:

      请注意,'echo "$var" | command 操作意味着标准输入仅限于回显的行。如果您还想连接终端,那么您需要更高级:

      { echo "$var"; cat - ; } | command
      
      ( echo "$var"; cat -   ) | command
      

      这意味着第一行将是$var 的内容,但其余的将来自cat 读取其标准输入。如果该命令没有做任何太花哨的事情(尝试打开命令行编辑,或者像vim 那样运行),那么它会很好。否则,您需要变得非常花哨 - 我认为 expect 或其派生词之一可能是合适的。

      命令行符号实际上是相同的 - 但第二个分号是必需的,大括号是必需的,而括号则不是。

      【讨论】:

      • ( echo "$LIST"; cat - ) | sed 1q 这对我有用,但我需要在运行此脚本时按 ctrl d?
      • 是的; cat - 继续从键盘读取直到 EOF 或中断,因此您需要通过键入 control-D 来告诉它 EOF。
      • 有没有办法绕过 EOF? sed 需要猫
      • 如果你不想要终端输入,你根本不必使用cat,就像第一行一样。或者您可以使用cat 列出文件。或者...如果您希望命令读取终端输入,则必须在它到达输入末尾时告诉它。或者你可以使用( echo "$var"; sed /quit/q - ) | command;这一直持续到您键入包含“退出”的行。您可以在处理它的方式上不断创新。当用户开始与厄瓜多尔合作时,当心程序停止工作的古老都市传说。他们会输入首都基多的名称,然后程序就退出了。
      • 好吧,如果你这么说。但为什么不只是echo "$LIST" | sed 1q | ...?这完全取决于你的目的。 &lt;&lt;EOF 符号应该要求在文件下方某处的行首单独使用 EOF。我认为我不会使用它——但我仍然不确定你想要实现什么。一个简单的echo "$LIST" | commandecho $LIST | command 在实践中可能就足够了(重要的是您要知道两者之间差异的重要性)。
      猜你喜欢
      • 1970-01-01
      • 2011-01-24
      • 1970-01-01
      • 2010-11-17
      • 1970-01-01
      • 2019-09-07
      • 2021-11-13
      • 2019-08-13
      • 1970-01-01
      相关资源
      最近更新 更多