Confusão sobre o comando bash vs. substituição de variável

0

Eu tenho escrito scripts bash de uma linha na linha de comando desde 1989. Eles geralmente têm a forma:

for name in 1 2 3; do wc $name.txt; done

Agora estou tentando fazer alguma manipulação de imagem com o GIMP, usando um script que leva dois nomes de arquivos para argumentos. Aqui está algo que me dá uma linha de comando válida:

/Applications/GIMP.app/Contents/MacOS/GIMP -b '(script-fu-overlay "png0004.tif" "png0000.tif") (script-fu-overlay "png0004.tif" "png0001.tif") (script-fu-overlay "png0004.tif" "png0002.tif") (script-fu-overlay "png0004.tif" "png0003.tif")'

Foi gerado com a ajuda de um típico liner:

X=\'$(for name in 0 1 2 3; do echo \(script-fu-overlay \"png0004.tif\" \"png000$name.tif\"\) ; done)\'

Mas ... e aqui está a minha pergunta:

echo /Applications/GIMP.app/Contents/MacOS/GIMP -b $X

me dá a linha de comando exata acima, mas quando eu corro

/Applications/GIMP.app/Contents/MacOS/GIMP -b $X

Eu recebo erros por não conseguir abrir nomes de arquivos com aspas ao seu redor, mas quando eu corro

/Applications/GIMP.app/Contents/MacOS/GIMP -b "$X"

tudo funciona. Meu objetivo real é entender se é possível escrever um one-liner sem recorrer a variáveis. Algo como:

/Applications/GIMP.app/Contents/MacOS/GIMP -b \'$(for name in 0 1 2 3; do echo \(script-fu-overlay \"png0004.tif\" \"png000$name.tif\"\) ; done)\'

Mas isso falha aparentemente da mesma maneira que

/Aplicativos/GIMP.app/Contents/MacOS/GIMP -b $ X

falha.

    
por Michael Tiemann 12.07.2017 / 15:30

3 respostas

2

Sim, você pode fazer isso na linha de comando sem recorrer a variáveis de shell.

/Applications/GIMP.app/Contents/MacOS/GIMP -b "$(for name in 0 1 2 3; do echo \(script-fu-overlay \"png0004.tif\" \"png000$name.tif\"\) ; done)"

Observe que precisamos eliminar as aspas simples de escape, pois não estamos fazendo um eval aqui. A única coisa que é necessária são as aspas duplas em torno da expansão do comando $ (...) para evitar que a divisão de palavras aconteça. PLUS passa o resultado como um único argumento para gimp .

    
por 12.07.2017 / 18:01
1

E sobre isso:

$ /Applications/GIMP.app/Contents/MacOS/GIMP -b \
"$(printf '%s\n' '(script-fu-overlay "png0004.tif" "png'{0..0004}'.png")')"
    
por 12.07.2017 / 21:36
1

man bash diz:

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and pathname expansion [...] After these expansions are performed, quote characters present in the original word are removed unless they have been quoted themselves (quote removal).

Assim, apenas as citações da palavra original são removidas. Mas essas citações só aparecem após a expansão e não serão removidas.

No caso dado, você pode deixá-los de lado, já que seus nomes de arquivos não precisam de nenhuma citação, mas você terá problemas com nomes de arquivos com espaços em branco ou outros caracteres especiais. E nem citações nem contrabarras o ajudarão, pois não são consideradas citações nesse estágio do processamento.

Editar para atender à solicitação "uma etapa":

Mas e se você tiver vários arquivos com espaços em branco como foo file foo e bar file bar e quiser cat alguns deles?

Sua linha de comando seria lida

cat "foo file foo" "bar file bar"

mas se houver mais, você vai querer fazer o script.

echo -n cat; for name in foo bar; do echo -n \ \"$name file $name\"; done

Produzirá exatamente a linha que você inseriria, mas

$(echo -n cat; for name in foo bar; do echo -n \ \"$name file $name\"; done)

falhará pelo motivo explicado acima. Colocar tudo entre aspas duplas (como ajudou no seu caso gimp ) também não funcionará, porque o novo comando precisa ser separado em palavras, mas não entre aspas.

Então, o que fazer para que as cotações funcionem como se fossem inseridas diretamente na linha de comando? Passe sua string como comando para o shell:

bash -c "$(echo -n cat; for name in foo bar; do echo -n \ \"$name file $name\"; done)"

Essa deve ser a abordagem mais geral para um one-liner executando um comando gerado.

    
por 12.07.2017 / 16:22