Resposta curta: veja BashFAQ # 50: Estou tentando colocar um comando em uma variável, mas os casos complexos sempre falham! .
Resposta longa: o shell faz a expansão de variáveis no meio do processo de análise de uma linha de comando - notavelmente, depois que ele processa aspas e escapa. Como resultado, colocar aspas e escapes em uma variável não faz o mesmo que tê-los diretamente na linha de comando.
A solução na sua resposta (dobrando os caracteres de escape) funcionará (na maioria dos casos), mas não pelo motivo pelo qual você acha que está funcionando, e isso me deixa bastante nervoso. O comando:
cmd="freebcp ... -t "," -r "\r\n" -c"
É analisado na cadeia de caracteres com aspas duplas freebcp ... -t
, seguido pela sequência de caracteres não citada ,
seguido pela sequência de caracteres com aspas duplas -r
, seguida pela sequência sem aspas '\\ r \\ n' (o fato que não é citado é por que você precisava dobrar as fugas), seguido pela cadeia entre aspas duplas '-c'. As aspas duplas que você quis fazer parte da string não são tratadas como parte da string, elas são tratadas como delimitadores que alteram o modo como partes diferentes da string são analisadas (e na verdade têm praticamente o contrário do efeito pretendido ). A razão pela qual isso funciona é que as aspas duplas na verdade não estavam tendo muito efeito no comando original, então reverter o efeito não fez muito. Na verdade, seria melhor removê-los (apenas os internos, no entanto), porque seria menos enganoso sobre o que realmente está acontecendo. Isso funcionaria, mas seria frágil - a única razão pela qual funciona é que você realmente não precisava das aspas duplas para começar, e se você tivesse uma situação (digamos, uma senha ou nome de arquivo com um espaço em que você realmente precisava de citações, você estaria em apuros.
Existem várias opções melhores:
-
Não armazene o comando em uma variável, apenas execute-o diretamente. Armazenar comandos é complicado (como você está descobrindo), e se você realmente não precisa, apenas não o faça.
-
Use uma função. Se você está fazendo algo como executar o mesmo comando sobre & acima, defina-o como uma função e use isso:
loaddb() { freebcp "${db_name}.dbo.${table_name}" in "${p_file}" -S "${server_name}" -U "${username}" -P "${password}" -t \",\" -r \"\r\n\" -c" } loaddb
Note que eu usei aspas duplas em torno de todas das referências a variáveis - isso é geralmente uma boa higiene de script, caso algum deles contenha espaços em branco, curingas ou qualquer outra coisa que o shell > faz analisar valores de variáveis.
-
Use uma matriz em vez de uma variável simples. Se você fizer isso corretamente, cada argumento de comando será armazenado como um elemento separado da matriz, e você poderá expandi-lo com o idioma
"${arrayname[@]}"
para obtê-lo intacto:cmdarray=(freebcp "${db_name}.dbo.${table_name}" in "${p_file}" -S "${server_name}" -U "${username}" -P "${password}" -t \",\" -r \"\r\n\" -c") "${cmdarray[@]}"
Novamente, observe o uso prolífico de aspas duplas; aqui eles estão sendo usados para garantir que os elementos da matriz sejam definidos corretamente, não como parte dos valores armazenados na matriz. Além disso, observe que os arrays não estão disponíveis em todos os shells; verifique se você está usando bash ou zsh ou algo semelhante.
Algumas notas finais: quando você usa algo como:
'somecommand'
as backquotes não estão fazendo o que você acha que são, e na verdade são potencialmente perigosas. O que eles fazem é executar o comando, capturar sua saída e tentar executar essa saída como outro comando . Se o comando não imprimir nada, isso não importa; mas se imprimir algo, é improvável que a saída seja um comando válido. Apenas perca as backquotes.
Por último, dar uma senha como um argumento de comando é insecure - argumentos de comando publicados na tabela de processos (por exemplo, o comandops
pode vê-los) e publicar senhas em locais públicos é uma péssima ideia. freebcp
parece não ter nenhuma maneira alternativa de fazer isso, mas eu encontrei um patch que permitirá que ele leia a senha de stdin ( echo "$password" | freebcp -P - ...
- note que echo
é um shell embutido, portanto seus argumentos não aparecem na tabela de processos). Eu não faço nenhuma reclamação sobre a correção, segurança, etc do patch (especialmente desde que é bastante antigo), mas eu iria verificar se eu fosse você.