Por que esse comando para copiar arquivos em um loop for funciona no bash, mas não no zsh?

3

Estou tentando copiar vários arquivos com o mesmo nome, mas em subdiretórios diferentes, para um único diretório, alterando os nomes para os baseados nos caminhos dos arquivos originais. Eu uso um loop for que faz o que eu pretendo bash, mas se comporta de forma muito estranha em zsh.

Comando

(quebras de linha adicionadas para legibilidade):

for f in */Flavors/*/stuff1/filename.txt;
    do l=$(echo $f | cut -d'/' --output-delimiter '' -f1,3);
    dest=/stuff2/${l}.utf8.txt;
    echo $f $dest;
    cp -v -- $f $dest;
done

Saída em zsh. Pretendo que os nomes dos arquivos de saída sejam, por exemplo, "EnglishAU.utf8.txt" mas em vez disso eles são apenas "Inglês", "Francês" e "Espanhol". Observe que o echo no loop mostra $dest contendo o caminho correto e, em seguida, o cp usa o caminho errado!

English/Flavors/AU/stuff1/filename.txt /stuff2/EnglishAU.utf8.txt
'English/Flavors/AU/stuff1/filename.txt' -> '/stuff2/English'
English/Flavors/UK/stuff1/filename.txt /stuff2/EnglishUK.utf8.txt
'English/Flavors/UK/stuff1/filename.txt' -> '/stuff2/English'
English/Flavors/US/stuff1/filename.txt /stuff2/EnglishUS.utf8.txt
'English/Flavors/US/stuff1/filename.txt' -> '/stuff2/English'
French/Flavors/CA/stuff1/filename.txt /stuff2/FrenchCA.utf8.txt
'French/Flavors/CA/stuff1/filename.txt' -> '/stuff2/French'
French/Flavors/FR/stuff1/filename.txt /stuff2/FrenchFR.utf8.txt
'French/Flavors/FR/stuff1/filename.txt' -> '/stuff2/French'
Spanish/Flavors/ES/stuff1/filename.txt /stuff2/SpanishES.utf8.txt
'Spanish/Flavors/ES/stuff1/filename.txt' -> '/stuff2/Spanish'
Spanish/Flavors/OT/stuff1/filename.txt /stuff2/SpanishOT.utf8.txt
'Spanish/Flavors/OT/stuff1/filename.txt' -> '/stuff2/Spanish'

Como mencionado acima, isso funciona como pretendido no bash. O que o zsh está fazendo?

    
por valrus 22.05.2014 / 22:58

1 resposta

8

Isso está acontecendo porque cut está exibindo caracteres NULL na saída. Não é possível transmitir argumentos do programa que contenham um caractere nulo (consulte ).

No bash, isso funciona porque o bash não pode manipular caracteres NULL em strings, e isso não acontece. O Zsh é um pouco mais poderoso e pode manipular caracteres NULL. No entanto, quando chega a hora de passar a string para o programa, ela ainda contém o nulo, que sinaliza o fim do argumento.

Vamos ver isso em detalhes.

$ echo 'English/Flavors/AU/stuff1/filename.txt' | cut -d'/' --output-delimiter '' -f1,3 | xxd
0000000: 456e 676c 6973 6800 4155 0a              English.AU.

Aqui simulamos um dos seus arquivos, passando o caminho por cut . Observe a saída xxd que tem um caractere NULL entre English e AU .

Agora vamos percorrer e simular o restante do script.

$ l=$(echo 'English/Flavors/AU/stuff1/filename.txt' | cut -d'/' --output-delimiter '' -f1,3)
$ dest=/stuff2/${l}.utf8.txt
$ echo "$dest" | xxd
0000000: 2f73 7475 6666 322f 456e 676c 6973 6800  /stuff2/English.
0000010: 4155 2e75 7466 382e 7478 740a            AU.utf8.txt.

Observe o NULL após o English . O echo exibe corretamente porque echo é um shell interno. Se usarmos um echo externo, ele também exibe o problema.

$ /bin/echo "$dest" | xxd 
0000000: 2f73 7475 6666 322f 456e 676c 6973 680a  /stuff2/English.

P.S. Você realmente deveria estar citando também: -)

A solução é não usar cut , use awk .

$ echo 'English/Flavors/AU/stuff1/filename.txt' | awk -F/ '{ print $1$3 }' | xxd
0000000: 456e 676c 6973 6841 550a                 EnglishAU.
    
por 22.05.2014 / 23:34

Tags