Eu tenho um comando que gera alguns parâmetros que desejo passar para outro comando. No entanto, quando executo o comando em um subshell, a saída está sujeita à divisão de palavras , a menos que eu cite tudo, caso em que é uma única palavra.
Quero que a saída do subshell tenha palavras divididas, mas quero que a divisão de palavras respeite as aspas, e isso não acontece. Existe alguma maneira (além de eval
) de dividir a saída do subshell em palavras, mas respeitando as aspas?
Detalhes
Dado o args
comando definido como
#!/bin/sh -
printf "%d args:" "$#"
printf " <%s>" "$@"
echo
eu posso correr
$ args 'foo bar' 'one two'
2 args: <foo bar> <one two>
mas não consigo encontrar uma maneira de fazer um subshell passar 2 argumentos assim.
$ echo "\"'foo bar'\" 'one two'"
"'foo bar'" 'one two'
$ args $(echo "\"'foo bar'\" 'one two'")
4 args: <"'foo> <bar'"> <'one> <two'>
$ args "$(echo "\"'foo bar'\" 'one two'")"
1 args: <"'foo bar'" 'one two'>
Claro, posso usareval
$ eval args $(echo "'foo bar' 'one two'")
2 args: <foo bar> <one two>
mas eval
é perigoso e apresenta todos os tipos de outras possibilidades assustadoras para as coisas darem errado. Não quero que a expansão de parâmetros ou globbing aconteça novamente, etc. Só quero que a divisão de palavras respeite as aspas. Não há realmente nenhuma maneira de fazer isso em bash
?
Bash realmente não tem uma boa maneira de analisar uma string em substrings, respeitando as aspas. Seja vindo de uma expansão de comando (ou seja,
$( )
-- o que eu acho que você está chamando de subshell) ou de uma expansão de variável simples ($varname
).eval
uma expansão com aspas duplas, ela analisa toda a sintaxe do shell, incluindo outros comandos e expansões de variáveis, redirecionamentos, vários comandos com;
ou&
, etc. Muitas oportunidades para resultados ruins aqui.eval
em uma expansão sem aspas, obterá o efeito de dividir em espaço em branco e expandir curingas, seguido por uma análise completa regular. Praticamente tudo pode dar errado aqui.read -a
é o melhor de um lote ruim. Ele falha completamente em respeitar as aspas, mas pelo menos não expande os curingas do nome do arquivo.Portanto, o próprio bash não pode fazer isso. Mas
xargs
pode - sua análise padrão de divisão em palavras respeita aspas e escapes, portanto, dependendo da situação, você pode usá-la diretamente:Existem alguns problemas potenciais com isso: Por um lado, dependendo de quanta saída existe,
xargs
pode decidir dividi-los entre várias execuções do comando. Você pode ajustar isso até certo ponto com suas opções-n
,-s
e-x
, mas não é totalmente satisfatório.Outro problema possível é que, se o comando for realmente uma função de shell, comando complexo ou interno que você deseja executar no shell atual, isso não funcionará. Você pode adaptá-lo, mas é confuso; você precisa usar
xargs printf '%s\0'
para converter em uma sequência de strings delimitada por nulo e, em seguida, usar umwhile IFS= read -r -d ''
loop para convertê-lo em uma matriz bash e, finalmente, você pode fazer algo com a matriz:Observe que isso usa uma substituição de processo com
<( )
. Este é um recurso apenas do bash e nem funcionará quando o bash estiver no modo de emulação sh. Portanto, você deve iniciar seu script com um bash shebang explícito (#!/bin/bash
ou#!/usr/bin/env bash
) (e não substitua o shebang executando o script comsh scriptname
).