Bash 反斜杠在反斜杠中到底是如何工作的?

Bash 反斜杠在反斜杠中到底是如何工作的?,bash,escaping,backslash,backticks,Bash,Escaping,Backslash,Backticks,从: 反勾号内的反斜杠(\)以不明显的方式处理: $ echo "`echo \\a`" "$(echo \\a)" a \a $ echo "`echo \\\\a`" "$(echo \\\\a)" \a \\a 但是FAQ并没有分解导致这种差异的解析规则。我找到的manbash中唯一相关的引用是: 当使用旧式的反引号替换形式时,反斜杠保留其字面含义,后面跟$、`、或除外 “$(echo\\a)”和“$

从:

反勾号内的反斜杠(\)以不明显的方式处理:

 $ echo "`echo \\a`" "$(echo \\a)"
 a \a
 $ echo "`echo \\\\a`" "$(echo \\\\a)"
 \a \\a
但是FAQ并没有分解导致这种差异的解析规则。我找到的
manbash
中唯一相关的引用是:

当使用旧式的反引号替换形式时,反斜杠保留其字面含义,后面跟$、`、或除外

“$(echo\\a)”
“$(echo\\\\a)”
的大小写非常简单:反斜杠,即转义字符,正在将自身转义为文字反冲。因此,
\\
的每个实例在输出中都成为
\
。但我很难理解backtick案例的类似逻辑。基本规则是什么?观察到的输出是如何遵循的

最后,一个相关的问题。。。如果不引用反勾号,则会出现“不匹配”错误:

这个案子发生了什么

更新 Re:我的主要问题是,我有一套解释所有行为的规则的理论,但仍然看不出它是如何从bash中任何记录的规则中得出的。以下是我提议的规则

在反斜杠内部,字符前面的反斜杠只返回该字符。也就是说,一个反斜杠没有效果。这适用于所有角色,除了反冲本身和反勾号。对于反斜杠本身,
\\
将成为转义反斜杠。它将逃避它的下一个角色

让我们通过一个例子来了解这一点:

a=xx
echo "`echo $a`"      # prints the value of $a
echo "`echo \$a`"     # single backslash has no effect: equivalent to above
echo "`echo \\$a`"    # escaping backslash make $ literal
印刷品:

xx
xx
$a

让我们从这个角度分析原始示例:

echo "`echo \\a`"
在这里,
\
产生一个转义反斜杠,但是当我们“转义”
a
时,我们只返回
a
,所以它打印
a

echo "`echo \\\\a`"

这里,第一对
\\
产生一个转义反斜杠,该反斜杠应用于
\
,产生一个文字反斜杠。也就是说,前3个
\\\
在输出中成为单个文本
\
。剩下的
\a
只产生
a
。最后的结果是,
\a

做了更多的研究,以找到所发生事情的参考和规律。来自it州

当使用旧式的反引号替换形式时,反斜杠 保留其字面含义,后跟“$”、“`”或“\”时除外。 第一个不带反斜杠的反引号终止命令 替代。使用$(命令)窗体时,所有字符 括号组成命令;没有人受到特殊对待

换句话说,\、\$和'inside of``在命令替换之前由CLI解析器处理。其他所有内容都传递给命令替换进行处理

让我们一步一步地看问题中的每个例子。在#之后,我介绍了在执行``或$()之前CLI解析器如何处理命令替换

你的第一个例子解释了

$ echo "`echo \\a`"   # echo \a
 a 
$ echo "$(echo \\a)"  # echo \\a
 \a
你的第二个例子解释了:

$ echo "`echo \\\\a`"   # echo \\a
 \a 
$ echo "$(echo \\\\a)"  # echo \\\\a
 \\a
你的第三个例子:

a=xx
$ echo "`echo $a`"    # echo xx 
xx
$ echo "`echo \$a`"   # echo $a
xx
echo "`echo \\$a`"    # echo \$a
$a
您使用$()的第三个示例


这样的逻辑很简单。所以我们看一下bash源代码(4.4)本身

subst.c:9273

case'`://*反引号命令替换*/
{
t_指数=sindex++;
temp=string\u extract(string,&sindex,“`”,SX\u REQMATCH);
/*根据t_索引测试sindex是为了允许
`通过,以便向后兼容*/
if(temp==&extract_string_error | | temp==&extract_string_fatal)
{
if(sindex-1==t_索引)
{
sindex=t_指数;
转到添加_字符;
}
最后一个命令退出值=执行失败;
报告_错误(u(“错误替换:在%s中没有结束“`”),字符串+t_索引);
自由(弦);
免费(istring);
返回((temp==&提取字符串错误)?&展开单词错误
:&展开\u word\u fatal);
}
如果(某物)
*扩展的某物=1;
如果(word->flags&W_NOCOMSUB)
/*sindex+1,因为字符串[sindex]='`'*/
temp1=子字符串(字符串,t_索引,sindex+1);
其他的
{
反斜杠(温度);
tword=命令替换(临时,引用);
temp1=tword?tword->word:(char*)空;
如果(第二)
处理单词描述(第二个);
}
免费(临时);
temp=temp1;
转到美元添加字符串;
}
如您所见,调用函数
de_反斜杠(temp)。相同功能的代码如下所示

subst.c:1607

/* Remove backslashes which are quoting backquotes from STRING.  Modifies
   STRING, and returns a pointer to it. */
char *
    de_backslash(string) char *string;
{
  register size_t slen;
  register int i, j, prev_i;
  DECLARE_MBSTATE;

  slen = strlen(string);
  i = j = 0;

  /* Loop copying string[i] to string[j], i >= j. */
  while (i < slen)
  {
    if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' ||
                              string[i + 1] == '$'))
      i++;
    prev_i = i;
    ADVANCE_CHAR(string, slen, i);
    if (j < prev_i)
      do
        string[j++] = string[prev_i++];
      while (prev_i < i);
    else
      j = i;
  }
  string[j] = '\0';

  return (string);
}
同样的输出是
\\$a
。现在让我们在bash中测试同样的功能

$a=xxx
$echo“$(echo\\$a)”
\xxx
$echo“`echo\\\\$a`”
\xxx

无法在bash 5.0.7上重现该错误,您有哪个版本?@oguzismail
GNU bash,5.0.2(1)版-发行版(x86\u 64-apple-darwin16.7.0)
该“不匹配”错误看起来像设置了
failglob
,出于某种原因,bash试图将
\a
视为文件名模式。我不知道它为什么会这样做;echo$(@KamilCuk)这是最近引入的一个bug。我不得不说,这相当令人失望,但我想,在如此复杂的软件工具下,它会发生。如果有这样的事情,可能值得向bash bug追踪器报告。
$ echo "$(echo $a)"     # echo $a
xx
$ echo "$(echo \$a)"    # echo \$a
$a
$ echo "$(echo \\$a)"   # echo \\$a
\xx
/* Remove backslashes which are quoting backquotes from STRING.  Modifies
   STRING, and returns a pointer to it. */
char *
    de_backslash(string) char *string;
{
  register size_t slen;
  register int i, j, prev_i;
  DECLARE_MBSTATE;

  slen = strlen(string);
  i = j = 0;

  /* Loop copying string[i] to string[j], i >= j. */
  while (i < slen)
  {
    if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' ||
                              string[i + 1] == '$'))
      i++;
    prev_i = i;
    ADVANCE_CHAR(string, slen, i);
    if (j < prev_i)
      do
        string[j++] = string[prev_i++];
      while (prev_i < i);
    else
      j = i;
  }
  string[j] = '\0';

  return (string);
}