bash quote escapando mudança em 4.4

6

Estou atualizando o bash em nossa plataforma integrada da versão 4.1.9 para a mais recente (4.4.12), e estou vendo uma mudança de comportamento neste cenário simples de passar argumentos de escape para um script.

Script / tmp / printarg:

#! /bin/sh
echo "ARG |$*|"

E eu invoco o script assim:

bash -c "/tmp/printarg \"abc\""

Eu tentei isso em várias plataformas (nativa x86_64 Linux) executando o bash 4.3.42, bem como várias plataformas embarcadas (ARM e PPC) rodando o bash 4.1.9 e 4.2.37, e todas essas plataformas relatam o que Eu esperaria:

38$ bash -c "/tmp/printarg \"abc\""
ARG |abc|

Mas, quando eu executo isso usando o bash 4.4.12 (plataformas X86 nativas ou embarcadas), eu entendo isso:

$ bash -c "/tmp/printarg \"abc\""
ARG |abc\|            <<< trailing backslash

E se eu adicionar um espaço na linha de comando entre a segunda cotação de escape e a cotação final, não vejo mais a barra invertida extra:

$ bash -c "/tmp/printarg \"abc\" "
ARG |abc |            <<< trailing space, but backslash is gone

Isso parece uma regressão. Alguma ideia? Eu também tentei ativar as várias opções de compatibilidade (compat40, compat41, compat42, compat43) com a mudança.

    
por Steve Hein 17.01.2018 / 20:26

3 respostas

9
bash -c "/tmp/printargs \"abc\""

Não escapa do que você acha que faz. Uma backslash-backslash é uma barra invertida de escape, manipulada pelo shell de chamada - de modo que é o mesmo que executar:

/tmp/printargs \abc\

porque as aspas duplas não são . Você poderia ter acabado de escrever:

bash -c '/tmp/printargs \abc\'

Suponho que você realmente queria:

bash -c "/tmp/printargs \"abc\""

que escapa as aspas duplas, passando um "abc" citado para o bash -c.

(Eu estou supondo que o comportamento diferente que você está vendo é diferentes versões do bash manipulando o nada escapado no final da entrada de forma diferente.)

Versão Perl de printargs (comportamento ligeiramente melhorado):

#!/usr/bin/perl
use feature qw(say);

for (my $i = 0; $i < @ARGV; ++$i) {
        say "$i: |$ARGV[$i]|";
}
    
por 17.01.2018 / 21:05
4
bash -c "/tmp/printargs \"abc\""

Tem certeza de que é isso que você quer fazer? Se você executar isso com set -x em vigor, verá que o comando que é executado é

bash -c '/tmp/printargs \abc\'

i.e. você está passando o shell uma string que termina em uma barra invertida. Sua primeira string entre aspas contém uma barra invertida de escape, então você tem uma abc sem aspas, uma barra invertida de escape e, em seguida, uma string entre aspas vazia. (Observe como o realce de sintaxe feito pelo Stackexchange mostra o abc não é citado).

A contra-barra invertida no final da entrada não faz muito sentido. Uma barra invertida escapa do caractere seguinte ou inicia uma linha de continuação, onde é excluída junto com a nova linha seguinte, como aqui:

$ bash -c $'echo "foo\\nbar"'                                                                                                              
foobar 

Este caso não tem nenhum. Você está possivelmente tentando fazer um destes:

bash -c "/tmp/printargs \"abc\""
bash -c '/tmp/printargs "abc"'

Ambos produzem a saída ARG |abc| .

Podemos ver a diferença entre os shells com um teste um pouco mais simples:

$ bash -c 'echo $BASH_VERSION; echo abc\'
4.4.12(1)-release
abc\

$ ./bash -c 'echo $BASH_VERSION; echo abc\'
4.3.30(1)-release
abc

$ dpkg -l dash |grep ^i
ii  dash           0.5.8-2.4    amd64        POSIX-compliant shell
$ dash -c 'echo abc\'
abc\

$ dpkg -l zsh |grep ^i
ii  zsh            5.3.1-4+b2   amd64        shell with lots of features
$ zsh -c 'echo abc\'
abc

Se eu tivesse que adivinhar, começaria a procurar a origem da alteração nessa alteração:

This document details the changes between this version, bash-4.4-alpha, and
the previous version, bash-4.3-release.

1.  Changes to Bash

cccc. Fixed a bug that resulted in short-circuited evaluation when reading
      commands from a string ending in an unquoted backslash, or when sourcing
      a file that ends with an unquoted backslash.

    
por 17.01.2018 / 20:59
1

Eu vou explicar isso:

$ bash -c "/tmp/quotefail \"abc\" "
ARG |abc |            <<< trailing space, but backquote is gone

Como o @ilkkachu explicou, com set -x vemos como isso é interpretado:

+ bash -c '/tmp/quotefail \abc\ '

Naturalmente, "\ a" é apenas "a" e "\" é apenas "", portanto o parâmetro / tmp / quotefail recebe é "abc" e o resultado:

ARG |abc |

No primeiro teste, a barra invertida não foi seguida por nada, então permaneceu como uma barra invertida.

    
por 17.01.2018 / 21:11