在Bash别名中转义字符

13

这是别名:

     # make a tmux session in working dir with the name of the dir
     alias th='tmux new -s $(pwd | tr '\/' '\n' | tail -n 1)'  

由于转义字符或别名中的单引号',它不能正常工作。将其打印出来。

    $ type --all th
    th is aliased to `tmux new -s $(pwd | tr / n | tail -n 1)'

看起来它只是将'\剥离了。

我最终通过将单引号更改为双引号来解决了这个问题。

     # make a tmux session in working dir with the name of the dir
     alias th='tmux new -s $(pwd | tr "\/" "\n" | tail -n 1)'  

我的问题是之前的代码怎么能正常工作?Bash不应该抛出解析错误吗?


2
虽然这并没有真正回答你的问题,但是考虑到你标记了bash,我会建议你使用 "${PWD##*/}" 来代替 $(pwd | ...) - Tom Fenech
相关链接:https://dev59.com/BWIj5IYBdhLWcg3wk182#20111135,以及http://stackoverflow.com/questions/40814087/escaping-single-quotes-inside-an-alias-in-bash - Charles Duffy
顺便提一下,虽然通常情况下 echo 是一个非常糟糕的调试工具(因为它有几种混淆数据的方式,而这些数据本来是要按原样显示的),但在这里它实际上足以显示问题:你会发现 echo 'tmux new -s $(pwd | tr '\/' '\n' | tail -n 1)' 没有显示任何内部引号,因为这些引号是语法上的而不是字面上的,在解析过程中被消耗掉了。 - Charles Duffy
2个回答

25

最佳建议:不要这样做。

使用一个函数代替:

th() { tmux new -s "${PWD##*/}" "$@"; }

${PWD##*/}是一个参数扩展,它从$PWD的内容中去除最后一个/及其之前的所有内容。


替代方案:字面量引号

您原始代码中的问题在于它包含了语法引号,这些引号由shell解析以确定单引号解析规则的开始和结束位置,而实际上您想要的是字面量引号,即被作为数据处理的引号(因此成为别名的一部分)。

使这些引号成为字面量的一种方法是使用$''引用形式,它允许您使用字面反斜杠来转义内部引号,使它们成为字面而不是语法:

alias th=$'tmux new -s $(pwd | tr \'\\\/\' \'\\n\' | tail -n 1)'

请注意,在使用$''时,字面上的反斜杠也需要转义,因此应写成\\而不是\

'$foo'"$((1+1))"baz

...$foo被单引号包裹,因此被视为字面字符串,$((1+1))被双引号包裹,因此有资格作为算术扩展被处理,而baz未被引用——尽管这三个变量连接起来形成了一个单词($foo2baz)。

这些引号都是语法性的——它们是对shell的指令——而不是字面意义的(如果是字面意义,那么它们将成为该字符串求值的一部分)。


这如何应用于您之前的命令

alias th='tmux new -s $(pwd | tr '\/' '\n' | tail -n 1)'  

tr命令的参数中使用单引号会导致之前定义的别名中的单引号终止。因此,在未加引号的上下文中评估\/\n(其中\/变成/\n变成n),由于如上所述,多个不同引用的子字符串可以连接成单个更大的字符串,你将获得以前的命令,而不是别名。


2

那么,一个Bash别名只是将字符串连接起来直到结束行?对我来说这很不直观。感谢您澄清事情。 - Charlie OConor
@JohnStone 嗯,在这里至关重要的是,没有分词空格(我实际上无意中在字符串列表中留下了空格)。如果您在列表中的任何位置具有未转义/引用的空格,则 bash 将处理这些空格作为不同的“单词”,但在此处,您只有一个单词,因为您拥有连续的字符串,其中一些部分被引用,而另一些是裸字符串(例如 \n)。 - Eric Renouf
@JohnStone,这不仅仅是“bash别名”的问题 - 所有POSIX兼容的shell语言(不仅仅是bash)都遵循相同的规则。 - Charles Duffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接