如何在 bash 中手动展开特殊变量(例如:~ 波浪线)
我的 bash 脚本中有一个变量,其值如下:
~/a/b/c
请注意,它是未展开的波形符。当我对此变量(称为 $VAR)执行 ls -lt 时,我没有得到这样的目录。我想让 bash 解释/扩展这个变量而不执行它。换句话说,我希望 bash 运行 eval 但不运行评估的命令。这在 bash 中可能吗?
我如何设法将其传递到我的脚本而不扩展?我用双引号将参数括起来。
尝试这个命令看看我的意思:
ls -lt "~"
这正是我所处的情况。我希望扩展波浪号。换句话说,我应该用什么替换 magic 以使这两个命令相同:
ls -lt ~/abc/def/ghi
并
ls -lt $(magic "~/abc/def/ghi")
注意 ~/abc/def/ghi 可能存在也可能不存在。
I have a variable in my bash script whose value is something like this:
~/a/b/c
Note that it is unexpanded tilde. When I do ls -lt on this variable (call it $VAR), I get no such directory. I want to let bash interpret/expand this variable without executing it. In other words, I want bash to run eval but not run the evaluated command. Is this possible in bash?
How did I manage to pass this into my script without expansion? I passed the argument in surrounding it with double quotes.
Try this command to see what I mean:
ls -lt "~"
This is exactly the situation I am in. I want the tilde to be expanded. In other words, what should I replace magic with to make these two commands identical:
ls -lt ~/abc/def/ghi
and
ls -lt $(magic "~/abc/def/ghi")
Note that ~/abc/def/ghi may or may not exist.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(19)
如果变量
var
是由用户输入的,则eval
不应该用于扩展波形符,原因是:用户可能会不小心使用(或按目的)输入例如
var="$(rm -rf $HOME/)"
可能会带来灾难性的后果。更好(更安全)的方法是使用 Bash 参数扩展:
If the variable
var
is input by the user,eval
should not be used to expand the tilde usingThe reason is: the user could by accident (or by purpose) type for example
var="$(rm -rf $HOME/)"
with possible disastrous consequences.A better (and safer) way is to use Bash parameter expansion:
由于 StackOverflow 的性质,我不能简单地让这个答案不被接受,但是在我发布这个答案的 5 年里,已经有比我公认的基本且相当糟糕的答案更好的答案了(我还年轻,不要别杀我)。
该线程中的其他解决方案是更安全、更好的解决方案。最好,我会选择这两个中的任何一个:
用于历史目的的原始答案(但请不要使用此)
如果我没记错的话,
"~"
不会被 bash 脚本以这种方式扩展,因为它被视为文字字符串"~"
。您可以像这样通过eval
强制扩展。或者,如果您想要用户的主目录,只需使用
${HOME}
即可。Due to the nature of StackOverflow, I can't just make this answer unaccepted, but in the intervening 5 years since I posted this there have been far better answers than my admittedly rudimentary and pretty bad answer (I was young, don't kill me).
The other solutions in this thread are safer and better solutions. Preferably, I'd go with either of these two:
Original answer for historic purposes (but please don't use this)
If I'm not mistaken,
"~"
will not be expanded by a bash script in that manner because it is treated as a literal string"~"
. You can force expansion viaeval
like this.Alternatively, just use
${HOME}
if you want the user's home directory.抄袭我自己的之前的答案,以便在不存在与
eval
相关的安全风险的情况下稳健地做到这一点:...用作...
或者,一种更简单的方法,仔细使用
eval
:Plagarizing myself from a prior answer, to do this robustly without the security risks associated with
eval
:...used as...
Alternately, a simpler approach that uses
eval
carefully:这是一个荒谬的解决方案:
解释此命令的作用:
bash
创建一个新的 bash 实例;"echo $var"
并将$var
替换为变量的值(因此在替换后,字符串将包含波形符);echo
并使用|
字符管道输出来完成此操作。基本上,我们正在运行的当前 bash 实例会取代我们作为另一个 bash 实例的用户的位置,并为我们输入命令“echo ~...”。
Here is a ridiculous solution:
An explanation of what this command does:
bash
;"echo $var"
and substitute$var
with the value of the variable (thus after the substitution the string will contain the tilde);echo
and piping its output with the|
character.Basically the current bash instance we're running takes our place as the user of another bash instance and types in the command
"echo ~..."
for us.这个怎么样:
或者:
How about this:
Or:
使用 eval 的安全方法是
"$(printf "~/%q" "$dangerous_path")"
。请注意,这是特定于 bash 的。有关详细信息,请参阅此问题
另外,请注意,在 zsh 下,这将与
echo ${~dangerous_path} 一样简单
A safe way to use eval is
"$(printf "~/%q" "$dangerous_path")"
. Note that is bash specific.See this question for details
Also, note that under zsh this would be as as simple as
echo ${~dangerous_path}
扩展(没有双关语)birriree 和 Halloleo 的答案:一般方法是使用
eval
,但它有一些重要的警告,即空格和输出重定向 (>
)在变量中。以下似乎对我有用:尝试使用以下每个参数:
解释
${mypath //>}
删除了可能会破坏 a 的>
字符在eval
期间文件。eval echo ...
是实际的波形符扩展。-e
参数周围的双引号用于支持带空格的文件名。也许有一个更优雅的解决方案,但这就是我能够想出的。
Expanding (no pun intended) on birryree's and halloleo's answers: The general approach is to use
eval
, but it comes with some important caveats, namely spaces and output redirection (>
) in the variable. The following seems to work for me:Try it with each of the following arguments:
Explanation
${mypath//>}
strips out>
characters which could clobber a file during theeval
.eval echo ...
is what does the actual tilde expansion-e
argument are for support of filenames with spaces.Perhaps there's a more elegant solution, but this is what I was able to come up with.
为什么不直接深入研究使用 getent 获取用户的主目录呢?
why not delve straight into getting the user's home directory with getent?
这是相当于 Håkon Hægland 的 Bash 答案
2017-12-10 编辑的 POSIX 函数:添加
'%s'
根据评论中的@CharlesDuffy。Here is the POSIX function equivalent of Håkon Hægland's Bash answer
2017-12-10 edit: add
'%s'
per @CharlesDuffy in the comments.供任何人参考,模仿 python os.path.expanduser() 行为的函数(无 eval 用法):
以及该函数:
For anyone's reference, a function to mimic python's os.path.expanduser() behavior (no eval usage):
And the function:
我相信这就是您正在寻找的
示例用法:
I believe this is what you're looking for
Example usage:
最简单:将“magic”替换为“eval echo”。
问题:您将遇到其他变量的问题,因为 eval 是邪恶的。例如:
请注意,第一次扩展时不会发生注入问题。因此,如果您只是将
magic
替换为eval echo
,应该没问题。但如果您执行 echo $(eval echo ~) ,则很容易受到注入。同样,如果您使用
eval echo ~
而不是eval echo "~"
,则将算作两次扩展,因此可以立即进行注入。Simplest: replace 'magic' with 'eval echo'.
Problem: You're going to run into issues with other variables because eval is evil. For instance:
Note that the issue of the injection doesn't happen on the first expansion. So if you were to simply replace
magic
witheval echo
, you should be okay. But if you doecho $(eval echo ~)
, that would be susceptible to injection.Similarly, if you do
eval echo ~
instead ofeval echo "~"
, that would count as twice expanded and therefore injection would be possible right away.在使用 read -e (以及其他)读取路径后,我已经通过变量参数替换来完成此操作。因此,用户可以使用 Tab 键补全路径,如果用户输入 ~ 路径,则会对其进行排序。
额外的好处是,如果没有波浪号,变量不会发生任何变化,如果有波浪号但不在第一个位置,它也会被忽略。
(我包含 -i 来读取,因为我在循环中使用它,以便用户可以在出现问题时修复路径。)
I have done this with variable parameter substitution after reading in the path using read -e (among others). So the user can tab-complete the path, and if the user enters a ~ path it gets sorted.
The added benefit is that if there is no tilde nothing happens to the variable, and if there is a tilde but not in the first position it is also ignored.
(I include the -i for read since I use this in a loop so the user can fix the path if there is a problem.)
这是我的解决方案:
Here's my solution:
只是为了扩展 birrryree 对带有空格的路径的回答:您不能将
eval
命令用作是因为它用空格分隔评估。一种解决方案是临时替换 eval 命令的空格:此示例当然依赖于
mypath
从未包含字符序列"_spc_"
的假设。Just to extend birryree's answer for paths with spaces: You cannot use the
eval
command as is because it seperates evaluation by spaces. One solution is to replace spaces temporarily for the eval command:This example relies of course on the assumption that
mypath
never contains the char sequence"_spc_"
.您可能会发现在 python 中这更容易做到。
(1) 从 unix 命令行:
结果:
(2) 在 bash 脚本中作为一次性 - 将其另存为
test.sh
:运行
bash ./test.sh< /code> 的结果是:
(3) 作为实用程序 - 将其另存为
expanduser
路径上的某处,并具有执行权限:然后可以在命令行上使用:
或在脚本中:
You might find this easier to do in python.
(1) From the unix command line:
Results in:
(2) Within a bash script as a one-off - save this as
test.sh
:Running
bash ./test.sh
results in:(3) As a utility - save this as
expanduser
somewhere on your path, with execute permissions:This could then be used on the command line:
Or in a script:
只需正确使用
eval
:进行验证。Just use
eval
correctly: with validation.由于某种原因,当字符串已经被引用时,只有 perl 可以挽救局面
for some reason when the string is already quoted only perl saves the day
我认为这
比所有其他解决方案更容易......或者我错过了一些东西?即使该路径并不真正存在,它仍然有效。
I think that
is easier than all the other solutions... or I am missing something? It works even if the path does not really exists.