A execução desse sudo -i echo $'line1\nline2'
sob strace mostra que o Bash é iniciado assim:
9183 execve("/bin/bash", ["-bash", "--login", "-c", "echo line1\\nline2\\n"], ...
Agora, strace
apresenta caracteres especiais com escapes invertidos quando exibe as strings, então o que o Bash realmente obtém como argumento para -c
é echo line1[backslash][newline]line2[backslash][newline]
e para o shell, uma barra invertida no final de uma linha marca uma linha de continuação e remove a barra invertida e a seguinte nova linha.
Sem -i
, sudo
executa echo
diretamente, sem passar pelo shell:
9189 execve("/bin/echo", ["echo", "line1\nline2\n"], ...
Aqui, uma nova linha literal vai para echo
e echo
imprime isso.
A ideia aqui deve ser que sudo
tente adicionar uma camada de shell escapando para acomodar o fato de que sh -c
toma uma string única , enquanto sudo
toma o comando como argumentos distintos.
Compare os seguintes casos:
sudo
escapa do espaço (este é apenas o nome do comando, sem argumentos!):
$ sudo -i 'echo foo'
-bash: echo foo: command not found
sudo
escapa da barra invertida, de modo que isso realmente funciona (o echo
do Bash não processa a barra invertida):
$ sudo -i echo 'foo\bar'
foo\bar
O mesmo com uma guia:
$ sudo -i echo $'foo\tbar'
foo bar
Aqui, não há nenhuma citação extra na barra invertida, então Bash a remove enquanto processa a linha de comando do shell ( b
não é um caractere especial para o shell, e não precisa ser citado. Isso é basicamente o mesmo que bash -c 'echo foo"b"ar'
):
$ bash -c 'echo foo\bar'
foobar
O problema é que você não pode escapar de uma nova linha com uma barra invertida, e sudo
não parece levar isso em conta.
Em qualquer caso, a citação de questões como essa provavelmente ficará um pouco mais fácil se você armazenar os comandos desejados em um arquivo e executá-los como um script.