【问题标题】:Bash- passing input without shell interpreting parameter expansion chars没有 shell 解释参数扩展字符的 Bash 传递输入
【发布时间】:2014-12-07 10:47:19
【问题描述】:

所以我有一个脚本,我在其中键入 script.sh,然后输入一组 if-else 语句。像这样:

    script.sh fnSw38h$?2

输出最终回显输入。

但我注意到 $?被解释为 0/1,所以输出会回显:

    fnSw38h12

如何阻止 shell 扩展字符并接受它的面值?

我查看了类似 opt noglob 或类似的东西,但它们没有用。 当我这样说的时候:

    script.sh 'fnSw38h$?2'

它有效。但是当我不能像 Var='$1' 那样在其中声明变量时,如何在单引号 ('') 中捕获它

请帮忙!

【问题讨论】:

  • echo \$? 会工作吗?
  • 它有效,但我无法控制脚本的输入,因为人们可以输入 $?运行脚本时。

标签: bash shell output parameter-passing


【解决方案1】:

您可以在双引号字符串中转义任何不打算引入参数扩展的美元符号。

var=foo
# Pass the literal string fnSw38h$?2foo to script.sh
script.sh "fnSw38h\$?2$var"

【讨论】:

  • 我意识到这一点,但运行脚本的人必须输入任何内容,包括 $?或 $# 或 & 但没有双引号或 \。所以只需 scriptname 后跟输入(不带引号或斜杠。)我正在专门寻找一种方法来做到这一点。
  • @BruceStrafford 这不太可能。您可以改为让脚本提示用户输入值并在不扩展的情况下读入。
【解决方案2】:

如何将密码传递给脚本

我从 cmets 获悉,此脚本的真正目的是验证密码。 如果这是一个重要或敏感的应用程序,您确实应该使用专业的安全工具。如果此应用程序不敏感或者这只是一个学习练习,那么请继续阅读以了解这些问题的初步介绍。

首先,不要这样做:

script.sh fnSw38h$?2

此密码将出现在ps 中,并且系统上的任何用户都可以看到纯文本

相反,让用户输入密码作为脚本的输入,例如:

#!/bin/sh
IFS= read -r var

在这里,read 将从键盘收集输入,不受外壳干扰,并且不会出现在 ps 输出中。

var 将有密码供您验证,但您确实不应该将纯文本密码保存在任何地方供您验证。最好将密码放入单向散列,然后将散列与您保存在文件中的内容进行比较。例如:

var=$(head -n1 | md5sum)

在这里,head 将读取一行(密码)并将其传递给md5summd5sum 会将其转换为哈希。该散列可以与该用户密码的已知正确散列进行比较。 head 返回的文本将与用户键入的内容完全相同,不会被 shell 破坏。

其实对于一个已知的hash算法,可以对常用密码做一个反向查表。因此,解决方案是创建一个名为salt 的变量,其中包含一些用户相关信息:

var=$( { head -n1; echo "$salt"; } | md5sum)

盐不必保密。它只是让查找表更难计算。

然而,md5sum 算法被发现有一些弱点。因此,它应该被更新的哈希算法所取代。在我写的时候,这可能是一个sha-2 变体。

同样,如果这是一个敏感的应用程序,请不要使用自制工具

回答原问题

当我不能像 Var='$1' 这样在其中声明变量时,如何在单引号 ('') 中捕获它

答案是你不需要。例如,考虑这个脚本:

#!/bin/sh
var=$1
echo $var

首先,注意$$$?都是shell变量:

$ echo $$ $? 
28712 0

现在,让我们试试我们的脚本:

$ bash ./script.sh '$$ $?'
$$ $?

这些变量没有展开是因为(1)它们出现在命令行时,它们是单引号,(2)在脚本中,它们被分配给变量并且bash没有展开变量递归地。换句话说,在echo $var 行上,bash 将扩展$var 以得到$$ $?,但它停止了。它不会扩展 var 中的内容。

【讨论】:

  • 但是我将输出保存到 $var,但是当回显它时,它似乎会像我上面发布的那样扩展 - 即使我将输入保存在变量中,fnSw38h$?2 也会变成 fnSw38h12 .
  • @BruceStrafford 我需要更多详细信息。如果你的结果与我的不同,我需要确切地知道你做了什么。
  • 没关系,我认为您可以在不涉及单引号的情况下将输入保存到 var。
  • @BruceStrafford 如果问题是输入没有单引号的密码,请查看我的更新答案。
【解决方案3】:

你不能做你想做的事。在命令行中输入的内容(例如脚本的参数)必须采用 shell 语法,并且在传递给脚本之前将由 shell 解释(根据 shell 的规则)。

当有人运行命令script.sh fnSw38h$?2 时,shell 将参数解析为文本“fnSw38h”,然后是$?,意思是“在这里替换最后一个命令的退出状态”,然后是“2”。所以 shell 会按照它被告知的那样做,它会替换最后一个命令的退出状态,然后将结果交给你的脚本。

您的脚本永远不会收到“fnSw38h$?2”,并且无法恢复该形式的参数。它会收到类似“fnSw38h02”或“fnSw38h12”的信息,因为这是用户要求外壳程序传递它的内容。这可能不是用户想要传递的,但正如我所说,命令必须是 shell 语法,并且在 shell 语法中,未转义和未引用的$? 表示“替换最后的退出状态这里”。

如果用户想传递“$?”作为参数的一部分,他们必须在命令行上转义或单引号。期间。

【讨论】:

  • 所以,假设有一个脚本可以在 bash 中验证密码。因此,一个人在终端中输入字符,脚本对其进行检查并提供输出。除了将字符放在引号之间之外,没有其他方法可以转义 shell 字符吗?
  • @BruceStrafford 有多种引用和转义样式可以使用,但如果在命令行上传递,则必须适当地引用/转义。请注意,如果您的脚本将其作为输入读取(即不在命令行上,而是作为单独的输入,例如read 命令),那么您可以控制它的解析方式。 IFS= read -r -p "Password: " password 会给你一个原始字符串(IFS= 防止空格修剪,-r 抑制一些转义解释)。此外,在引用变量时使用双引号(例如echo "$password")以防止出现其他问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-03
  • 1970-01-01
相关资源
最近更新 更多