Exactly how do backslashes work within backticks?

前端 未结 2 866
谎友^
谎友^ 2021-01-04 10:38

From the Bash FAQ:

Backslashes (\\) inside backticks are handled in a non-obvious manner:

 $ echo "`echo \\\\a`" "$(echo \\         


        
2条回答
  •  北海茫月
    2021-01-04 11:10

    The logic is quite simple as such. So we look at bash source code (4.4) itself

    subst.c:9273

    case '`': /* Backquoted command substitution. */
    {
        t_index = sindex++;
    
        temp = string_extract(string, &sindex, "`", SX_REQMATCH);
        /* The test of sindex against t_index is to allow bare instances of
            ` to pass through, for backwards compatibility. */
        if (temp == &extract_string_error || temp == &extract_string_fatal)
        {
        if (sindex - 1 == t_index)
        {
            sindex = t_index;
            goto add_character;
        }
        last_command_exit_value = EXECUTION_FAILURE;
        report_error(_("bad substitution: no closing \"`\" in %s"), string + t_index);
        free(string);
        free(istring);
        return ((temp == &extract_string_error) ? &expand_word_error
                                                : &expand_word_fatal);
        }
    
        if (expanded_something)
        *expanded_something = 1;
    
        if (word->flags & W_NOCOMSUB)
        /* sindex + 1 because string[sindex] == '`' */
        temp1 = substring(string, t_index, sindex + 1);
        else
        {
        de_backslash(temp);
        tword = command_substitute(temp, quoted);
        temp1 = tword ? tword->word : (char *)NULL;
        if (tword)
            dispose_word_desc(tword);
        }
        FREE(temp);
        temp = temp1;
        goto dollar_add_string;
    }
    

    As you can see calls a function de_backslash(temp); on the string which updates the string in c. The code the same function is below

    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);
    }
    

    The above just does simple thing if there is \ character and the next character is \ or backtick or $, then skip this \ character and copy the next character

    So if convert it to python for simplicity

    text = r"\\\\$a"
    
    slen = len(text)
    i = 0
    j = 0
    data = ""
    while i < slen:
        if (text[i] == '\\' and (text[i + 1] == '`' or text[i + 1] == '\\' or
                                 text[i + 1] == '$')):
            i += 1
        data += text[i]
        i += 1
    
    print(data)
    

    The output of the same is \\$a. And now lets test the same in bash

    $ a=xxx
    
    $ echo "$(echo \\$a)"
    \xxx
    
    $ echo "`echo \\\\$a`"
    \xxx
    

提交回复
热议问题