bash içinde dizeyi birleştirerek build komutu


14

Bir seferde yürütmeden önce bazı parametrelere dayalı bir dize bir komut satırı oluşturur bir bash komut dosyası var. Komut dizesine birleştirilen parçaların, her bileşen boyunca bir veri akışını kolaylaştırmak için borularla ayrılması beklenir.

Çok basitleştirilmiş bir örnek:

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

Nedense borular çalışmıyor. Bu komut dosyasını çalıştırdığımda genellikle komutun ilk bölümüne (ilk kanaldan önce) ilişkin farklı hata mesajları alıyorum.

Benim sorum şu: Bu şekilde bir komut oluşturmanın mümkün olup olmadığı ve bunu yapmanın en iyi yolu nedir?


Hata mesajları nelerdir?
CameronNemo

Senaryomda (bu basitleştirmeden biraz daha karmaşık) "dosya bulunamadı"
Lennart Rolland

infileGeçerli dizinde var olduğunu varsaymak güvenli midir?
saiarcot895

Evet. kodumda wget -O - dosya yerine. Aslında, ben sadece bitmiş dize kopyalayıp terminalde pasete eğer iyi çalışır
Lennart Rolland

Yanıtlar:


17

Her şey işlerin ne zaman değerlendirileceğine bağlıdır. Yazdığınızda $cmd, satırın geri kalan kısmı içindeki ilk sözcüğe bağımsız değişken olarak geçirilir $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  

Bu, echokomuta iletilen bağımsız değişkenlerin " /etc/passwd", " |" (dikey çubuk karakteri), " wc" ve " -l" olduğunu gösterir.

Gönderen 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

Bunun bir çözümü, ileride başvurmak üzere "eval" kullanmaktır. Bu, dizenin bash tarafından nasıl yorumlandığını unutturmasını ve her şeyin doğrudan bir kabuğa yazıldığını okumasını sağlar (tam olarak istediğimiz şeydir).

Yani yukarıdaki örnekte,

$cmd

ile

eval $cmd

çözüldü.


Belirtilen parametrelerle dikkatli olun. eval foo "a b"ile aynı olurdu eval foo "a" "b".
udondan

2

@waltinator bunun neden beklediğiniz gibi çalışmadığını zaten açıkladı. Başka bir yolu bash -ckomutunu yürütmek için kullanmaktır :

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

1
Parsimony bana başka bir işlem başlatmamamı bash -c, ancak evalgeçerli işlemde komutu yapmak için kullanmamı söylüyor.
waltinator

@waltinator eminim, ben muhtemelen eval için de kullanacağım (bu yüzden seni ve Lennart'ı iptal ettim). Sadece bir alternatif veriyorum.
terdon

0

Muhtemelen bunu yapmanın daha iyi bir yolu, evalbir Bash dizisi kullanmaktan ve kullanmaktan kaçınmaktır ve tüm argümanları oluşturmak ve bunları komuta karşı yürütmek için satır içi genişletmektir.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.