Como posso verificar os caracteres verbatim de uma string de comando bash?

15

Eu tive esse comportamento estranho esta manhã em um terminal:

user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
bash: [: missing "]"
user@home:/home/user$ [ -f /etc/openvpn/client.conf ] && echo true
true
  • O primeiro comando foi colado em um script editado com o gedit.
  • O segundo foi digitado diretamente no terminal.

Após algumas pesquisas, descubro que remover o 30º caractere (o espaço entre client.conf e "]") e substituí-lo por um espaço fez o comando funcionar novamente.

Minha suposição estava correta: um caractere em branco desconhecido entrou no comando , mas a pergunta é:

  1. Como posso revelar esses caracteres no terminal para que eu possa depurar o comando? E mais importante:
  2. Como posso evitar que isso aconteça novamente?

BTW, eu rodando o Ubuntu 18.04 / idioma francês, o script do qual eu colei o comando está em uma unidade USB e pode ter sido editado no Windows também.

Obrigado por suas respostas muito boas. O caractere ruim é um caractere UTF-8 de c2 a0 sem espaço. A pergunta Como remover um caracter especial 'M-BM-' com sed tem um fato interessante sobre esse personagem.

O estranho é que o roteiro está livre desse personagem. Então eu não sei de onde isso veio.

    
por Gabriel Glenn 16.05.2018 / 11:08

4 respostas

11

Uma opção é observar os caracteres que você está tentando usar com um visualizador ou editor hexadecimal. hexdump é uma boa opção se você estiver limitado ao terminal.

$ hexdump -Cv <<"EOF"
> [ -f /etc/openvpn/client.conf ] && echo true
> EOF
00000000  5b 20 2d 66 20 2f 65 74  63 2f 6f 70 65 6e 76 70  |[ -f /etc/openvp|
00000010  6e 2f 63 6c 69 65 6e 74  2e 63 6f 6e 66 20 5d 20  |n/client.conf ] |
00000020  26 26 20 65 63 68 6f 20  74 72 75 65 0a           |&& echo true.|
0000002d

Você pode ver aqui que os space , close-square-brace , space estão corretos - 0x20 , 0x5D , 0x20 .

Esses valores são códigos ASCII, exibidos em hexadecimal . Qualquer valor fora do intervalo 0x20 - 0x7E não é um " caractere imprimível " no que diz respeito ao ASCII, e provavelmente não funcionará bem com interfaces de linha de comando.

Observação: copiei sua primeira linha " quebrada " para usar no exemplo hexdump acima, então algo substituiu o not-an-ASCII -space com um espaço ASCII entre sua fonte original e sua pergunta renderizada.

Para repetir isso, siga os seguintes passos:

  1. Digite hexdump -Cv <<"EOF" e pressione Enter
  2. Cole o texto que você gostaria de usar
  3. Digite EOF em uma linha própria e pressione Enter

Terminais e Interfaces de Linha de Comando não lidam bem com caracteres especiais - como você descobriu. Se você não é muito cuidadoso com a formatação de documentos, você também terá problemas com o Microsoft Word (e outros) usando " aspas inteligentes ", em traços, a lista continua ...

Descubra a diferença: (a parte superior é " citações inteligentes ", a parte inferior é " citações diretas ")

$hexdump-Cv<<"EOF"
> “quoted string”
> EOF
00000000  e2 80 9c 71 75 6f 74 65  64 20 73 74 72 69 6e 67  |...quoted string|
00000010  e2 80 9d 0a                                       |....|
00000014

Aqui, as aspas abertas não são uma simples cotação ASCII ( " ), mas são um Unicode / UTF-8 série - 0xE2 , 0x80 , 0x9C ou U+201C - que o terminal não vai funcionar como você poderia esperar.

A sugestão de Kiwy de cat -A também faz o trabalho:

$ cat -A <<"EOF"
> “quoted string”
> EOF
M-bM-^@M-^\quoted stringM-bM-^@M-^]$

Observação: ao usar echo "..." | hd , você tem a chance de que o bash substitua partes da string que você está tentando inspecionar. Isso é particularmente preocupante ao tentar inspecionar componentes de um script.

Por exemplo, tente:

$ echo "${USER}"
attie

$ echo "'whoami'"
attie

$ echo "$(whoami)"
attie

$ cat <<EOF
> ${USER}
> EOF
attie

Esses métodos estão substituindo componentes pelo texto relevante. Para evitar isso, use uma das seguintes abordagens. Observe o uso de aspas simples ( ' ) e um " citado heredoc " ( "EOF" ).

$ echo '${USER}'
${USER}

$ echo ''whoami''
'whoami'

$ echo '$(whoami)'
$(whoami)

$ cat <<"EOF"
> ${USER}
> EOF
${USER}
    
por 16.05.2018 / 11:30
18

Você pode usar cat com a opção -A : no manual:

   -A, --show-all
          equivalent to -vET
   -E, --show-ends
          display $ at end of each line
   -T, --show-tabs
          display TAB characters as ^I
   -v, --show-nonprinting
          use ^ and M- notation, except for LFD and TAB

Então, cat -A yourscrip.sh mostrará caracteres invisíveis e estranhos.

    
por 16.05.2018 / 11:33
9

echo "<your command>" | hd deve funcionar. Procure por backspace (0x08) ou caracteres com códigos > = 80. echo "<your command>" | wc -b e verificar se a contagem corresponde ao que você vê também é uma boa ideia.

Copiar material de arquivos produzidos por qualquer coisa com "Office" em seu nome é perigoso, porque esse software geralmente toma a liberdade de substituir caracteres: em francês, procure por aspas duplas substituídas por "guillemets", em inglês para citações simples substituídos por seus equivalentes abertos / próximos. O mais difícil que eu encontrei foi um espaço de não largura de 0 de largura no meio de um nome de arquivo (3 dias de inatividade do servidor ...).

    
por 16.05.2018 / 11:34
2

Bash, e outras shells como zsh, podem abrir a linha de comando atual em um editor. O atalho padrão para o bash é C-x C-e ( Ctrl X Ctrl E ), e é aberto no primeiro disponível de $VISUAL , $EDITOR e emacs. Na prática, isso é inestimável para depurar e modificar comandos complexos. Dependendo de como você olha para ele, o zsh é mais amigável que o bash: quando o editor sai, bash executa o comando imediatamente, enquanto o zsh espera que você pressione Enter (dando a você mais chances de editar o comando). comando).

Após abrir o comando em um editor, você pode configurar seus editores para mostrar caracteres não-ASCII de maneira diferente.

Por exemplo, com o Vim , usando estas configurações:

set encoding=latin1
set isprint=
set display+=uhex

Ou,adaptandoosmétodosdasoutrasrespostas:

bash-4.4$f(){cat-A"$@"; false; }   # exit false to prevent bash from running the command
bash-4.4$ VISUAL=f
bash-4.4$ [ -f /etc/openvpn/client.conf ] && echo true  # C-x C-e here
[ -f /etc/openvpn/client.confM-BM- ] && echo true$
    
por 17.05.2018 / 11:12