comando build concatenando la stringa in bash


13

Ho uno script bash che crea una riga di comando in una stringa basata su alcuni parametri prima di eseguirlo in una volta sola. Le parti concatenate alla stringa di comando dovrebbero essere separate da pipe per facilitare uno "streaming" di dati attraverso ciascun componente.

Un esempio molto semplificato:

#!/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

Per qualche ragione i tubi non sembrano funzionare. Quando eseguo questo script ricevo diversi messaggi di errore relativi in ​​genere alla prima parte del comando (prima della prima pipe).

Quindi la mia domanda è se è possibile o meno costruire un comando in questo modo, e qual è il modo migliore per farlo?


Quali sono i messaggi di errore?
Cameron Nemo il

Nel mio script (che è un po 'più complesso di questa semplificazione) ottengo "file not found"
Lennart Rolland

È sicuro supporre che infileesista nella directory corrente?
saiarcot895,

sì. nel mio codice è wget -O - invece di un file. In realtà, se copio solo la stringa concatenata e la passo nel terminale, funziona bene
Lennart Rolland,

Risposte:


17

Tutto dipende da quando le cose vengono valutate. Durante la digitazione $cmd, l'intero resto della riga viene passato come argomenti alla prima parola in $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  

Ciò mostra che gli argomenti passati al echocomando sono: " /etc/passwd", " |" (il carattere della barra verticale), " wc" e " -l".

Da 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

Una soluzione a questo, per riferimento futuro, è usare "eval". Questo assicura che qualunque modo la stringa venga interpretata da bash venga dimenticata e l'intera lettura viene letta come se fosse digitata direttamente in una shell (che è esattamente ciò che vogliamo).

Quindi nell'esempio sopra, sostituendo

$cmd

con

eval $cmd

risolto.


Fai attenzione però con i parametri citati. eval foo "a b"sarebbe lo stesso di eval foo "a" "b".
udondan,

2

@waltinator ha già spiegato perché questo non funziona come previsto. Un altro modo per aggirarlo è usare bash -cper eseguire il tuo comando:

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

1
La parsimonia mi dice di non iniziare un altro processo con bash -c, ma usare evalper eseguire il comando nel processo corrente.
Waltinator,

@waltinator certo, probabilmente userò anche eval per questo (motivo per cui ho votato tu e Lennart). Sto solo fornendo un'alternativa.
terdon,

0

Forse un modo migliore per farlo è quello di evitare di usare evale semplicemente usare un array Bash ed è l'espansione in linea per creare tutti gli argomenti e quindi eseguirli contro il 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.