citações em torno de nomes de arquivos confundem script bash

2

Estou escrevendo um script que aplica um comando a vários arquivos. A maneira como eu automatizo isso se resume a

#!/bin bash
for in_file in ${PWD}/*/*/*.txt; do
    out_file="${in_file//in/out}"
    CM="/usr/local/bin/command -in \"${in_file}\" -out \"${out_file}\""
    echo ${CM}
    ${CM}
done

A saída típica que obtenho é

/usr/local/bin/command -in "/home/user/infile.txt" -out "/home/user/outfile.txt"
error:
/usr/local/bin/command: unable to find file "/home/user/infile.txt"
...

Mas então eu cortei & cole o mesmo comando, verbatim, incluindo todas as aspas, etc., no prompt de comando, e ele é executado sem problemas!

Como muitos nomes de arquivos (que eu não criei) possuem espaços, preciso das aspas no script. Mas alguém pode me dizer por que eles não funcionam no script e funcionam no prompt de comando? E existe uma maneira de fazer isso corretamente?

    
por alle_meije 25.02.2014 / 15:38

2 respostas

4

Use uma matriz: essa é a melhor maneira de preservar um comando com várias palavras com argumentos que contêm espaço em branco.

#!/bin bash
for in_file in ${PWD}/*/*/*.txt; do
    out_file="${in_file//in/out}"
    cmd=( /usr/local/bin/command -in "$in_file" -out "$out_file" )
    echo "${cmd[@]}"
    "${cmd[@]}"
done
    
por 25.02.2014 / 15:57
1

"$ {CM}" não usará caracteres em branco como separadores. Então o bash acha que a string inteira é o nome do seu programa.

Com o $ {CM} bash, pense que "são parte do argumento.

Você pode usar < < eval "$ {CM}" > > mas você pode ter efeitos colaterais com caracteres especiais sem escape nos nomes dos arquivos.

Então é melhor o seguinte:

Isso funciona?

#!/bin bash
for in_file in "$PWD"/*/*/*.txt; do
    out_file="$in_file/in/out"
    CM="/usr/local/bin/command -in \"$in_file\" -out \"$out_file\""
    echo "${CM}"
    /usr/local/bin/command -in "$in_file" -out "$out_file"
done

Em vez de repetir seus comandos, use "set -x" para depurar seu script:

#!/bin bash
set -x
for in_file in "$PWD"/*/*/*.txt; do
    out_file="$in_file/in/out" 
    /usr/local/bin/command -in "$in_file" -out "$out_file"
done

ou isto:

#!/bin bash
for in_file in "$PWD"/*/*/*.txt; do
    out_file="$in_file/in/out" 
    set -x
    /usr/local/bin/command -in "$in_file" -out "$out_file"
    set +x
done
    
por 25.02.2014 / 15:52