Quando você deixa uma substituição de variável $var
ou uma substituição de comando $(cmd)
unquoted, o resultado passa pelas seguintes transformações:
- Divida a string resultante em palavras. A divisão acontece no espaço em branco (sequências de espaço, tabulações e novas linhas); isso pode ser configurado definindo
IFS
(consulte 1 , 2 ).
- Cada palavra resultante é tratada como um padrão glob e, se corresponder a alguns arquivos, a palavra é substituída pela lista de nomes de arquivos.
Observe que o resultado não é uma string, mas uma lista de strings. Além disso, observe que caracteres de cotação como "
não estão envolvidos aqui; eles fazem parte da sintaxe da fonte do shell, não da expansão de strings.
Uma regra geral de programação de shell é sempre colocar aspas duplas em torno das substituições de comando e variável, a menos que você saiba por que você precisa deixá-las desligadas. Então, em test.sh
, escreva echo "Argument 1: $1"
. Para passar argumentos para test.sh
, você está com problemas: você precisa passar uma lista de palavras de args.sh
para test.sh
, mas o método escolhido envolve uma substituição de comando e fornece apenas o caminho para uma string simples.
Se você pode garantir que os argumentos a serem passados não contêm nenhuma nova linha, e é aceitável alterar ligeiramente o processo de invocação, você pode definir IFS
para conter apenas uma nova linha. Certifique-se de que args.sh
produza exatamente um nome de arquivo por linha sem aspas erradas.
IFS='
'
test.sh $(args.sh)
unset IFS
Se os argumentos puderem conter caracteres arbitrários (presumivelmente exceto os bytes nulos, que você não pode passar como argumentos), você precisará executar alguma codificação. Qualquer codificação serve; é claro, não será o mesmo que passar os argumentos diretamente: isso não é possível. Por exemplo, em args.sh
(substitua \t
por um caractere de tabulação real se seu shell não for compatível):
for x; do
printf '%s_\n' "$x" |
sed -e 's/q/qq/g' -e 's/ /qs/g' -e 's/\t/qt/g' -e 's/$/qn/'
done
e em test.sh
:
for arg; do
decoded=$(printf '%s\n' "$arg" |
sed -e 's/qs/ /g' -e 's/qt/\t/g' -e 's/qn/\n/g' -e 's/qq/q/g')
decoded=${decoded%_qn}
# process "$decoded"
done
Você pode preferir alterar test.sh
para aceitar uma lista de strings como entrada. Se as strings não contiverem novas linhas, invoque args.sh | test.sh
e use isso em args.sh
( explicação ):
while IFS= read -r line; do
# process "$line"
done
Outro método que impede a necessidade de citar completamente é invocar o segundo script a partir do primeiro.
…
args.sh "$@"