Não-ascii na linha de comando bash do Cygwin causa erro

Não-ascii na linha de comando bash do Cygwin causa erro

Estou chamando o bash do cmd.exe assim

c:\cygwin\bin\bash --login -c "echo ф"

e entre no Cygwin 2.8.0

/usr/bin/bash: echo ф: command not found

Ele trata o parâmetro como parte do nome do comando. Fazendo o mesmo no Cygwin 2.5.2, recebo a saída ф.

Responder1

Como isso costumava funcionar e funciona bem para pessoas executando o bash no Unices (testei no Debian aqui), acho que você encontrou um bug do Cygwin. O projeto Cygwin tem uma página sobrerelatando bugs do Cygwin. Eles têm um monte de informações e etapas úteis, longas demais para serem resumidas aqui.

Enquanto isso, suspeito que você possa contornar isso escapando do personagem. O Bash echo, quando recebe o -esinalizador, interpreta várias sequências de escape:

c:\cygwin\bin\bash --login -c "echo -e '\xd1\x84'"

Deveria trabalhar. Hexadecimal D1 84é a codificação UTF-8 de ф. Se você tiver a unicodeferramenta, ela lhe dirá - mas também apenas ecoará o caractere para odou xxd:

$ echo -n 'ф' | od -t x1
0000000 d1 84
0000002

$ echo -n 'ф' | xxd -p
d184

O FAQ do Cygwin me diz que ele usa UTF-8 por padrão, então isso deve funcionar. Mas é claro que você também pode usar outras codificações (achoO Windows usa principalmente UTF16le):

$ echo -n 'ф' | iconv -t utf16le | xxd -p
4404

Responder2

Isso acontece porque cmd.exeadiciona um par adicional de aspas em torno de argumentos com caracteres não ASCII. Então o que realmente chega na aplicação cygwin é o seguinte:

C:\cygwin\bin\bash --login -c "echo blo"
arg0: /usr/bin/bash
arg1: --login
arg2: -c
arg3: echo blo

Então o bash pode interpretar ' echo blo', mas:

C:\cygwin\bin\bash --login -c "echo blöd"
arg0: /usr/bin/bash
arg1: --login
arg2: -c
arg3: "echo blöd"

Agora o bash não reconhece ' "echo blöd"'.

Responder3

A análise do user1274247 é a correta.

Portanto, precisamos encontrar uma maneira de eliminar as aspas iniciais e finais, quando cmd.exe as duplica sem nosso conhecimento.

Eu estava tendo exatamente o mesmo problema ao lidar com caminhos contendo espaços, aspas simples e caracteres não ASCII. Eu resolvi isso separando o comando bash para executar (também conhecido como -c) e o parâmetro de string problemático.

De acordo com bash man:

-c string
If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0. 

Nosso comando, portanto, torna-se:

C:\cygwin\bin\bash.exe -lc 'a="${0%\"}"; a="${a#\"}";echo "$a"; sleep 10' "C:\spa ces\quo'tes\nonàscîï"

Ou, se preferir usar o terminal mintty:

C:\cygwin\bin\mintty.exe /bin/bash -lc 'a="${0%\"}"; a="${a#\"}";echo "$a"; sleep 10' "C:\spa ces\quo'tes\nonàscîï"

E se você quiser usá-lo no regedit (para iniciar um comando bash ao clicar com o botão direito em um arquivo), aqui está o escape adequado (para aspas e%):

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Classes\*\shell\Test]
@="echo filename in bash"

[HKEY_CURRENT_USER\Software\Classes\*\shell\Test\command]
@="C:\\cygwin\\bin\\mintty.exe /bin/bash -lc ' a=\"${0%%\\\"}\"; a=\"${a#\\\"}\";echo \"$a\"; sleep 10 ' \"%1\""

Estou usando, por exemplo, para duplicar arquivos (extrair de uma caixa de ferramentas maior):

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Classes\*\shell\Duplicate]
@="Duplicate file"

[HKEY_CURRENT_USER\Software\Classes\*\shell\Duplicate\command]
@="C:\\cygwin\\bin\\mintty.exe /bin/bash -lc ' a=\"${0%%\\\"}\"; a=\"${a#\\\"}\" ; cd \"$(dirname \"$(cygpath \"$a\")\")\"; f=\"$(basename \"$a\")\" ; n=\"$(basename \"$a\" \".${f##*.}\")\" ; cp \"${f}\" \"${n}-copy.${f##*.}\"   ' \"%1\" "

informação relacionada