如何扩展波浪符号〜作为变量的一部分?

安德鲁

当我打开bash提示并键入:

$ set -o xtrace
$ x='~/someDirectory'
+ x='~/someDirectory'
$ echo $x
+ echo '~/someDirectory'
~/someDirectory

我希望上面的第五条线会过去的+ echo /home/myUsername/someDirectory有没有办法做到这一点?在我最初的Bash脚本中,实际上是通过如下循环从输入文件中的数据填充变量x:

while IFS= read line
do
    params=($line)
    echo ${params[0]}
done <"./someInputFile.txt"

不过,我得到了类似的结果,echo '~/someDirectory'而不是echo /home/myUsername/someDirectory

白狼

POSIX标准按照下面的顺序来进行强加字膨胀(强调的是矿):

  1. 应从头至尾执行波浪扩展(请参见波浪扩展),参数扩展(请参见参数扩展),命令替换(请参见命令替换)和算术扩展(请参见算术扩展)。请参阅令牌识别中的第5项。

  2. 除非IFS为空,否则应对由步骤1生成的部分字段执行字段拆分(请参见“字段拆分”)。

  3. 除非set -f有效,否则应执行路径名扩展(请参阅路径名扩展)。

  4. 报价删除(请参见报价删除)应始终最后执行。

这里唯一使我们感兴趣的一点是第一个:如您所见,波浪号展开是在参数展开之前进行的:

  1. Shell尝试在上展开代字号echo $x,但找不到代字号,因此继续进行。
  2. Shell尝试对进行参数扩展echo $x$x找到并扩展,命令行变为echo ~/someDirectory
  3. 处理继续,已经处理过的波浪符号扩展~符保持原样。

通过在分配时使用引号$x,您明确要求不要扩展波浪号并将其视为普通字符。经常错过的一件事是,在shell命令中,您不必引用整个字符串,因此您可以使扩展在变量赋值期间发生:

user@host:~$ set -o xtrace
user@host:~$ x=~/'someDirectory'
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$

您还可以使扩展在echo命令行上进行,只要它可以参数扩展之前进行即可

user@host:~$ x='someDirectory'
+ x=someDirectory
user@host:~$ echo ~/$x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$

如果由于某种原因您确实需要在$x不扩展情况下影响代字号,并且能够在echo命令中对其进行扩展,则必须分两次进行以强制$x发生变量的两次扩展

user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ echo "$( eval echo $x )"
++ eval echo '~/someDirectory'
+++ echo /home/user/someDirectory
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$ 

但是,请注意,根据使用这种结构的上下文,它可能会产生有害的副作用。根据经验,最好避免使用eval其他方式需要的任何东西

如果您要专门解决波浪号问题而不是其他任何扩展,那么这种结构将更安全,更可移植:

user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ case "$x" in "~/"*)
>     x="${HOME}/${x#"~/"}"
> esac
+ case "$x" in
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$ 

此结构显式检查前导的存在,~如果找到了,则将其替换为用户主目录。

在您发表评论之后,x="${HOME}/${x#"~/"}"对于那些没有在shell编程中使用过但实际上与我上面引用的POSIX规则相关联的人来说,这确实可能令人惊讶。

根据POSIX标准的规定,引号删除发生在最后,参数扩展发生在很早。因此,${#"~"}在对外部报价进行评估之前对进行了评估和扩展。依次,如参数扩展规则中所定义

在每种情况下都需要单词的值(基于参数的状态,如下所述),单词应进行波浪号扩展,参数扩展,命令替换和算术扩展。

因此,#必须正确引号或转义运算符的右侧,以避免波浪线扩展。

因此,换句话说,当shell解释器查看时x="${HOME}/${x#"~/"}",他看到:

  1. ${HOME}并且${x#"~/"}必须扩展。
  2. ${HOME}扩展到$HOME变量的内容
  3. ${x#"~/"}触发嵌套扩展:"~/"被解析,但是被引用,被当作字面量1您可能在此处使用单引号产生了相同的结果。
  4. ${x#"~/"}现在,表达式本身已展开,导致~/从的值中删除了前缀$x
  5. 上面的结果现在被连接起来:的扩展${HOME},字面量/,扩展${x#"~/"}
  6. 最终结果用双引号引起来,从功能上防止了单词拆分。我在这里功能说,因为这些双引号不是技术上需要(见这里那里的实例),但作为个人风格,只要一个任务得到任何超出a=$b我通常发现有更清晰的加双引号。

顺便说一句,如果更仔细地看一下case语法,您将看到该"~/"*结构依赖于x=~/'someDirectory'我上面解释的相同概念(此处,双引号和单引号可以互换使用)。

不用担心这些事情乍一看似乎是晦涩的(甚至在第二次或以后的情况下也是如此!)。我认为,使用子外壳程序进行参数扩展是使用外壳程序语言进行编程时要掌握的最复杂的概念之一。

我知道有些人可能会强烈反对,但是如果您想更深入地学习Shell编程,我建议您阅读Advanced Bash Scripting Guide:它教Bash脚本,因此具有许多扩展和特色。与POSIX shell脚本相比,虽然有些吹毛求疵,但是我发现它写得很好,并带有大量实际示例。一旦解决了这个问题,就很容易在需要时将自己限制为POSIX功能,我个人认为直接进入POSIX领域对于初学者来说是不必要的陡峭学习曲线(将我的POSIX波浪号替换为@ m0dular的类似于regex的Bash进行比较)等同于了解我的意思;)!)。


1:这使我发现Dash中的一个错误,该错误未在此处正确实现波浪号扩展(可使用验证x='~/foo'; echo "${x#~/}")。对于用户和Shell开发人员而言,参数扩展都是一个复杂的领域!

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

使用变量作为语句的一部分

如何通过PHP在JSON添加@符号作为对象名的一部分

如何使用变量内容作为其他变量名称的一部分?

如何通过一个环境变量作为命令的一部分

React TypeScript:如何使用变量名作为键的一部分

如何使用系统环境变量作为@PropertySource值的一部分?

Rails-如何使用变量作为方法名称的一部分

如何使用函数参数作为变量的一部分?

作为Xcode构建过程的一部分,如何运行上传符号来上传dSYM?

如果不是菜单的一部分,如何在PowerPoint中插入特殊字符作为项目符号?

jQuery text()抓取“ <”符号作为文本的一部分

作为结构调用的一部分,如何在一行中编写变量名称?

局部变量作为全局变量的一部分

VSIX 部署片段作为扩展的一部分

如何在 Concourse CI 中使用 yaml 模板变量作为值的一部分

如何更改变量中存在的路径字符串(作为循环的一部分)?

如何在angularJs 1.5中将$ scope变量作为组件的templateUrl的一部分传递?

如何在接口块内指定模块变量作为伪参数声明的一部分?

如何获得字符串的一部分,直到某个符号,然后在符号后的部分作为单独的值

更改符号颜色SVG的一部分

在作为回调一部分的嵌套函数中传递或访问变量

Python-使用变量作为字符串格式的一部分

JMeter预处理变量作为报告文件名的一部分

使用模板变量作为脚本src的一部分

Django-作为包含的一部分传递flatpage.title变量

在WHILE循环中使用变量作为列名的一部分

合并多个列,将变量名作为数据的一部分

使用已定义的变量作为R中路径名的一部分

Swift-变量在if语句中作为更新的一部分,但随后将还原