Por que o número de barras invertidas aumenta no strace ao invés de ser reduzido pelas regras do bash?

2

Esta questão está relacionada com Como imprimo barra invertida seguida de nova linha com printf? , onde o OP tenta imprimir \\n como barra invertida simples seguida de nova linha (não literal \n ).

Enquanto por regras shell, faz sentido que \ seja expandido como \ e \n como n (ou seja, o shell executa escape de barra invertida para preservar a forma literal do caractere seguinte), quando eu executo strace parece que o shell executa algo totalmente diferente e estou com dificuldades para interpretar o que estou vendo.

$ strace -e execve printf "\\n"
execve("/usr/bin/printf", ["printf", "\\n"], [/* 42 vars */]) = 0
\n+++ exited with 0 +++

Em outras palavras, o que estou vendo é que, em vez do número reduzido de caracteres que entra em argv porção de execve syscall, o número realmente aumenta e há um acréscimo de uma barra invertida extra.

A passagem do único percentual '\\n' é ainda mais confusa:

$ strace -e execve printf '\\n'
execve("/usr/bin/printf", ["printf", "\\\\n"], [/* 42 vars */]) = 0
\n+++ exited with 0 +++

Em outras palavras, eu esperaria que aqui o shell passaria tudo inalterado para printf da mesma forma que em execve() do comando anterior e obteria a mesma saída que printf "\\n" , mas é diferente.

Até certo ponto pareço circular em torno do entendimento de que pure printf em si (aquele executado pelo sistema) interpretaria o argumento \\n colocado em argv porção de execve() como barra invertida e nova linha. Enquanto isso, o shell precisaria converter \\n digitado pelo usuário para corresponder às suas próprias regras, mas estou lutando para verbalizar o que exatamente está acontecendo com várias barras invertidas.

    
por Sergiy Kolodyazhnyy 23.01.2018 / 18:53

4 respostas

4

strace exibe a string na sintaxe da string C, em que uma única barra invertida é mostrada como \ , uma nova linha como \n e assim por diante.

O que é passado para execve é o que a função puts imprime quando recebe a string literal que strace imprime como argumentos no código-fonte C.

    
por 23.01.2018 / 19:59
0

O shell está fazendo muito pouco com suas cadeias que são delimitadas por aspas duplas, já que o único escape que se aplica é o escape de \ para \ antes de passar o argumento para o programa invocado (um dos poucos que escapam regras que se aplicam em strings de aspas duplas), e nada para suas strings que são delimitadas por aspas simples.

strace , por outro lado, está tentando escrever algo parecido com o código-fonte C, embora não seja exatamente como as chamadas da função de linguagem C se pareçam, escapando de \ s.

Então:

  • Em
    % strace -e execve printf '!\n'
    execve("/usr/bin/printf", ["printf", "!\n"], [/* 35 vars */]) = 0
    !
    +++ exited with 0 +++
    %
    , o argumento passado para strace e daí para printf é exatamente a cadeia longa de três caracteres !\n . strace está imprimindo como uma constante de string de linguagem C, onde \ dentro da string é duplicada, gerando ".\n" . É printf que está interpretando \n para indicar um avanço de linha, é claro.
  • Em
    % strace -e execve printf '!\\n'
    execve("/usr/bin/printf", ["printf", "!\\\n"], [/* 35 vars */]) = 0
    !\
    +++ exited with 0 +++
    %
    exatamente o mesmo está acontecendo, salve que haja mais \ s para dobrar na string da linguagem C e printf esteja reconhecendo \ seguido por \n .
  • Em
    % strace -e execve printf '!\\n'
    execve("/usr/bin/printf", ["printf", "!\\\\n"], [/* 35 vars */]) = 0
    !\n+++ exited with 0 +++
    %
    exatamente o mesmo está acontecendo, salve que haja mais \ s para dobrar na string da linguagem C e printf esteja reconhecendo \ seguido por \ seguido por n .
  • Em
    % strace -e execve printf \!"\\n"
    execve("/usr/bin/printf", ["printf", "!\\n"], [/* 35 vars */]) = 0
    !\n+++ exited with 0 +++
    %
    , o shell está reduzindo o argumento para !\n devido às regras de escape para palavras com aspas duplas na linguagem shell que reduzem \ e as cota \! para impedir o reconhecimento do caractere de expansão do histórico; a string da linguagem C é, portanto, "!\\n" ; e printf está vendo \ seguido por n .
  • Em
    % strace -e execve printf \!$'
    % strace -e execve printf '!\n'
    execve("/usr/bin/printf", ["printf", "!\n"], [/* 35 vars */]) = 0
    !
    +++ exited with 0 +++
    %
    7'"\\n" execve("/usr/bin/printf", ["printf", "!\\n"], [/* 35 vars */]) = 0 !\n+++ exited with 0 +++ %
    , o mesmo acontece, exceto que a forma escapada da linguagem C do caractere não se parece nada com a do shell, que está usando uma terceira forma de cotação.
por 23.01.2018 / 20:06
0

Como mencionado por outros, strace imprime os argumentos da chamada do sistema citados da mesma maneira que caracteres especiais são citados em C. Uma nova linha é representada como \n , uma barra invertida literal como \ , etc. href="http://man7.org/linux/man-pages/man1/strace.1.html"> página man )

Character pointers are dereferenced and printed as C strings. Non-printing characters in strings are normally represented by ordinary C escape codes.

Provavelmente é mais fácil usar set -x para ver o que o shell envia para o comando em execução. O Bash coloca a saída do xtrace entre aspas simples e as barras invertidas não surtem efeito nelas.

No seu primeiro exemplo:

$ set -x
$ printf "\\n" > /dev/null
+ printf '\n'

A primeira barra invertida escapa da segunda, produzindo um único \ . A terceira barra invertida não escapa de nada, já que é seguida por uma letra que não precisa escapar, então é tomada literalmente. E a carta também é usada literalmente, então nós recebemos \n . C-citado, as barras invertidas são duplicadas.

Os caracteres que são escapados por uma barra invertida entre aspas duplas estão explicitamente listados no padrão , eles são o sinal de dólar $ , backtick ' , aspas duplas", barra invertida propriamente dita, \ e nova linha.

O ponto de exclamação é especial devido à expansão do histórico, mas se for escapado, a barra invertida que a precede não é removida no Bash . No entanto, o Zsh o remove se a expansão do histórico estiver ativada.

    
por 23.01.2018 / 21:22
0

O número de barras invertidas é de fato reduzido pelo shell (em uma instância). mas expressa como uma barra invertida dupla em execve "C-citando".

Entre aspas duplas (man dash):

Double Quotes Enclosing characters within double quotes preserves the literal meaning of all characters except dollarsign ($), backquote ('), and backslash (\). The backslash inside double quotes is historically weird, and serves to quote only the following characters:
$ ' " \ <newline>.
Otherwise it remains literal.

Então, na linha:

$ strace -e execve printf "\\n"

o shell altera os argumentos um pouco, e é isso que strace recebe:

$ strace -e execve printf "\ \n"     # Space added for emphasis, not real.

Apenas a primeira barra invertida citará a próxima barra invertida. Então, o mecanismo de aspas de execve, onde a string é expressa como uma string "C-quoted", dobrará o número de barras invertidas usadas, duas se tornam quatro:

$ execve("/usr/bin/printf", ["printf", "\\n"], [/* 42 vars */]) = 0

É isso que se vê.

Entre aspas simples (man dash):

Single Quotes
Enclosing characters in single quotes preserves the literal meaning of all the characters (except single quotes, making it impossible to put single-quotes in a single-quoted string).

Então, strace recebe algo como essa string:

strace -e execve printf '\ \ \ \ n'    #space(s) added for emphasis.

As quatro (4) barras invertidas se tornam oito (8) quando "c-citado" por execve:

execve("/usr/bin/printf", ["printf", "\\\\n"], [/* 42 vars */]) = 0

Corresponde ao que é visto nos seus exemplos.

    
por 24.01.2018 / 02:37

Tags