comando build concatenando a string no bash


13

Eu tenho um script bash que cria uma linha de comando em uma seqüência de caracteres com base em alguns parâmetros antes de executá-lo de uma só vez. As partes que são concatenadas para a cadeia de comandos devem ser separadas por pipes para facilitar um "fluxo" de dados através de cada componente.

Um exemplo muito simplificado:

#!/bin/bash
part1=gzip -c
part2=some_other_command
cmd="cat infile"

if [ ! "$part1" = "" ]
then
    cmd+=" | $part1"
fi


if [ ! "$part2" = "" ]
then
    cmd+=" | $part2"
fi


cmd+="> outfile"
#show command. It looks ok
echo $cmd
#run the command. fails with pipes
$cmd

Por alguma razão, os canos não parecem funcionar. Quando executo esse script, recebo diferentes mensagens de erro relacionadas geralmente à primeira parte do comando (antes do primeiro canal).

Portanto, minha pergunta é se é possível ou não criar um comando dessa maneira, e qual é a melhor maneira de fazê-lo?


Quais são as mensagens de erro?
precisa saber é o seguinte

No meu script (que é um pouco mais complexo do que esta simplificação) eu recebo "arquivo não encontrado"
Lennart Rolland

É seguro supor que infileexista no diretório atual?
precisa saber é o seguinte

sim. no meu código é wget -O - em vez de um arquivo. Na verdade, se eu apenas copiar a cadeia concatenada e pasete-lo no terminal funciona muito bem
Lennart Rolland

Respostas:


17

Tudo depende de quando as coisas são avaliadas. Quando você digita $cmd, o restante da linha é passado como argumento para a primeira palavra em $cmd.

walt@spong:~(0)$ a="cat /etc/passwd"
walt@spong:~(0)$ b="| wc -l"
walt@spong:~(0)$ c="$a $b"
walt@spong:~(0)$ echo $c
cat /etc/passwd | wc -l
walt@spong:~(0)$ $c
cat: invalid option -- 'l'
Try 'cat --help' for more information.
walt@spong:~(1)$ eval $c
62
walt@spong:~(0)$ a="echo /etc/passwd"
walt@spong:~(0)$ c="$a $b"
walt@spong:~(0)$ echo $c
echo /etc/passwd | wc -l
walt@spong:~(0)$ $c
/etc/passwd | wc -l
walt@spong:~(0)$ $c |od -bc
0000000 057 145 164 143 057 160 141 163 163 167 144 040 174 040 167 143  
          /   e   t   c   /   p   a   s   s   w   d       |       w   c  
0000020 040 055 154 012  
              -   l  \n  
0000024
walt@spong:~(0)$ eval $c
1  

Isso mostra que os argumentos passados ​​para o echocomando são: " /etc/passwd", " |" (o caractere da barra vertical), " wc" e " -l".

De man bash:

eval [arg ...]  
    The  args  are read and concatenated together into   
    a single command.  This command is then read and  
    executed by the shell, and its exit status is returned  
    as the value of eval.  If there are no args, or only null  
    arguments, eval returns 0.

8

Uma solução para isso, para referência futura, é usar "eval". Isso garante que, de qualquer maneira que a string seja interpretada pelo bash, seja esquecida e a coisa toda seja lida como se tivesse sido digitada diretamente em um shell (que é exatamente o que queremos).

Portanto, no exemplo acima, substituindo

$cmd

com

eval $cmd

Resolvi-o.


Tenha cuidado, porém, com os parâmetros citados. eval foo "a b"seria o mesmo que eval foo "a" "b".
Udondan

2

O @waltinator já explicou por que isso não funciona como o esperado. Outra maneira de contornar isso é usar bash -cpara executar seu comando:

$ comm="cat /etc/passwd"
$ comm+="| wc -l"
$ $comm
cat: invalid option -- 'l'
Try 'cat --help' for more information.
$ bash -c "$comm"
51

1
A parcimônia me diz para não iniciar outro processo bash -c, mas use evalpara executar o comando no processo atual.
waltinator

@waltinator claro, eu provavelmente usaria eval para isso também (e foi por isso que votei em você e em Lennart). Estou apenas fornecendo uma alternativa.
precisa

0

Possivelmente, a melhor maneira de fazer isso é evitar o uso evale apenas o uso de uma matriz Bash e sua expansão embutida para criar todos os argumentos e executá-los no comando.

runcmd=() # This is slightly messier than declare -a but works
for cmd in $part1 $part2 $part3; do runcmd+="| $cmd "; done
cat infile ${runcmd[@]} # You might be able to do $basecmd ${runcmd[@]}
# but that sometimes requires an `eval` which isn't great
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.