o bash não pode armazenar o valor hexadecimal 0x00 na variável

11

Estou tentando fazer alguns truques com dd. Eu pensei que seria possível armazenar alguns valores hexadecimais em uma variável chamada "cabeçalho" para canalizá-lo em dd.

Meu primeiro passo sem uma variável foi este:

$ echo -ne "\x36\xc9\xda\x00\xb4" |dd of=hex
$ hd hex

00000000  36 c9 da 00 b4                                    |6....|
00000005

Depois disso, tentei o seguinte:

$ header=$(echo -ne "\x36\xc9\xda\x00\xb4") 
$ echo -n $header | hd

00000000  36 c9 da b4                                       |6...|
00000004

Como você pode ver, perdi meu \x00valor na $headervariável. Alguém tem uma explicação para esse comportamento? Isto está me enlouquecendo.

Frank
fonte
Eu entendo bash: warning: command substitution: ignored null byte in input.
Kusalananda
Faltam aspas, mas deve ser o header="$(echo -ne "\x36\xc9\xda\x00\xb4")"; echo -n "$header" | hdmesmo resultado.
Ctrl-alt-delor
Isso funciona header="\x36\xc9\xda\x00\xb4"; echo -n "$header" | hd, mas não é a mesma coisa que está armazenando a forma legível por humanos.
Ctrl-alt-delor

Respostas:

16

Você não pode armazenar um byte nulo em uma sequência porque o Bash usa sequências no estilo C, que reservam o byte nulo para terminadores. Portanto, você precisa reescrever seu script para simplesmente canalizar a sequência que contém o byte nulo, sem que o Bash precise armazená-lo no meio. Por exemplo, você pode fazer isso:

printf "\x36\xc9\xda\x00\xb4" | hd

Observe, a propósito, que você não precisa echo; você pode usar o Bash's printfpara muitas outras tarefas simples.

Ou, em vez de encadear, você pode usar um arquivo temporário:

printf "\x36\xc9\xda\x00\xb4" > /tmp/mysequence
hd /tmp/mysequence

Obviamente, isso tem o problema de o arquivo /tmp/mysequencejá existir. E agora você precisa continuar criando arquivos temporários e salvando seus caminhos em strings.

Ou você pode evitar isso usando a substituição de processo:

hd <(printf "\x36\xc9\xda\x00\xb4")

O <(command)operador cria um pipe nomeado no sistema de arquivos, que receberá a saída de command. hdreceberá como primeiro argumento o caminho para esse canal - que ele abrirá e lerá quase como qualquer arquivo. Você pode ler mais sobre isso aqui: /unix//a/17117/136742 .

Giusti
fonte
1
Embora correto, esse é um detalhe da implementação e não o motivo exato. Eu olhei para ele, e o padrão POSIX realmente exige esse comportamento, então você tem o motivo real . (Como alguns já apontaram, zshfarão isso, mas apenas no modo nōn-POSIX.) Na verdade, olhei para ele porque estava me perguntando se valia a pena implementá-lo em mksh
mirabilos
@mirabilos, você gostaria de expandir isso? AFAICT, o comportamento não é especificado por POSIX para substituição de comando quando a saída possui caracteres NUL, e para zsh no modo POSIX, a única diferença relevante em que consigo pensar é que na shemulação \0não está no valor padrão de $IFS. echo "$(printf 'a\0b')"ainda funciona bem na shemulação em zsh.
Stéphane Chazelas
3
@mirabilos Considerando que os shells são anteriores ao padrão POSIX por uma década ou mais, acho que você pode descobrir que o motivo real é que os shells usavam cordas no estilo C e o padrão foi construído em torno disso.
giusti
Encontrei um bom Q para uma discussão detalhada sobre printf versus eco. unix.stackexchange.com/questions/65803/…
Paulb
8

Você pode usar o zshque é o único shell que pode armazenar o caractere NUL em suas variáveis. Por acaso, esse caractere está no valor padrão de $IFSin zsh.

nul=$'\0'

Ou:

nul=$'\x0'

Ou

nul=$'\u0000'

Ou

nul=$(printf '\0')

No entanto, observe que você não pode passar uma variável como argumento ou variável de ambiente para um comando que é executado, pois os argumentos e as variáveis ​​de ambiente são sequências delimitadas por NUL passadas para a execve()chamada do sistema (uma limitação da API do sistema, não do shell ) No zshentanto, você pode passar NUL bytes como argumentos para funções ou comandos internos.

echo $'\0' # works
/bin/echo $'\0' # doesn't
Stéphane Chazelas
fonte
1
"Você pode usar o zsh". Não, obrigado - estou ensinando a mim mesmo o bash-script como iniciante no momento. Não quero me confundir com outra sintaxe. Mas obrigado Veray muito para sugerir isso
Frank
Por uma questão de fato, você usou a zshsintaxe em sua pergunta. echo -n $headersignifica passar o conteúdo da $headervariável como último argumento para echo -né zsh(ou fishou rcou es) sintaxe, não bash sintaxe. Em bash, isso tem um significado muito diferente . Geralmente, zshé como ksh( basho shell GNU, sendo mais ou menos parte do clone do kshshell de fato do Unix), mas com a maioria das idiossincrasias de design do shell Bourne corrigidas (e muitos recursos extras e muitos mais fácil de usar / menos surpreendente).
Stéphane Chazelas
Cuidado: o zsh pode alterar um byte zero às vezes: echo $(printf 'ab\0cd') | od -vAn -tx1c imprime `61 62 20 63 64 0a`, que é um espaço em que um NUL deve existir.
sorontar 24/02
1
E isso é algo que nenhum outro shell (nulo) reproduzirá. Isso faz com que um script se comporte de maneiras muito especiais no zsh. Na minha opinião: zsh está apenas tentando ser inteligente demais.
sorontar
2
Tendo "corrigido" as características incorretas do projeto presentes no padrão POSIX sh, de que se acostumar a escrever scripts zsh significa que você está se acostumando a práticas que seriam bugs se exercitadas em qualquer outro shell. Esse não é um problema com uma sintaxe tão diferente de um idioma diferente que habilidades ou hábitos provavelmente não serão transferidos, mas esse não é o caso em questão.
Charles Duffy