команда build шляхом об'єднання рядка в bash


13

У мене є скрипт bash, який будує командний рядок у рядку на основі деяких параметрів, перш ніж виконувати його за один раз. Частини, об'єднані в командний рядок, повинні бути розділені трубами для полегшення "потокової передачі" даних через кожен компонент.

Дуже спрощений приклад:

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

Чомусь труби, здається, не працюють. Коли я запускаю цей скрипт, я отримую різні повідомлення про помилки, які зазвичай стосуються першої частини команди (перед першою трубкою).

Отже, моє питання полягає в тому, чи можна таким чином побудувати команду, і який найкращий спосіб це зробити?


Що таке повідомлення про помилку?
CameronNemo

У моєму сценарії (який є дещо складнішим за це спрощення) я отримую "файл не знайдено"
Lennart Rolland

Чи безпечно припустити, що infileіснує в поточному каталозі?
saiarcot895

так. у моєму коді це wget -O - замість файлу. Насправді, якщо я просто скопіюю об'єднаний рядок і вставте його в термінал, він працює нормально
Lennart Rolland

Відповіді:


17

Все залежить від того, коли речі будуть оцінені. Під час введення $cmdвесь решта рядка передається як аргументи до першого слова в $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  

Це показує, що аргументи, передані echoкоманді: " /etc/passwd", " |" (символ вертикальної смуги), " wc" і " -l".

Від 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

Одним із варіантів цього рішення для подальшого використання є використання "eval". Це забезпечує те, що будь-який спосіб інтерпретації рядка bash буде забутий, і вся річ читається так, ніби вона була набрана безпосередньо в оболонці (саме цього ми хочемо).

Так у наведеному вище прикладі замінимо

$cmd

з

eval $cmd

вирішили це.


Будьте уважні, хоча з цитованими параметрами. eval foo "a b"було б те саме, що eval foo "a" "b".
Udondan

2

@waltinator вже пояснив, чому це не працює, як ви очікували. Ще один спосіб її використання - bash -cце виконувати команду:

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

1
Парсимент каже мені не починати інший процес bash -c, а використовувати evalдля виконання команди в поточному процесі.
waltinator

@waltinator впевнений, я б, напевно, використовував і eval для цього (саме тому я підтримав вас і Леннарта). Я просто пропоную альтернативу.
terdon

0

Можливо, кращий спосіб зробити це - уникнути використання evalта просто використання масиву Bash, і це вбудоване розширення, щоб зібрати всі аргументи, а потім виконати їх проти команди.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.