xây dựng lệnh bằng cách nối chuỗi trong bash


13

Tôi có một tập lệnh bash xây dựng một dòng lệnh trong một chuỗi dựa trên một số tham số trước khi thực hiện nó trong một lần. Các phần được nối với chuỗi lệnh được cho là được phân tách bằng các đường ống để tạo điều kiện "truyền phát" dữ liệu qua từng thành phần.

Một ví dụ rất đơn giản:

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

Vì một số lý do, các đường ống dường như không hoạt động. Khi tôi chạy tập lệnh này, tôi nhận được các thông báo lỗi khác nhau liên quan đến phần đầu tiên của lệnh (trước ống đầu tiên).

Vì vậy, câu hỏi của tôi là liệu có thể xây dựng một lệnh theo cách này hay không, và cách tốt nhất để làm điều đó là gì?


Các thông báo lỗi là gì?
CameronNemo

Trong kịch bản của tôi (có phần phức tạp hơn đơn giản hóa này) tôi nhận được "không tìm thấy tệp"
Lennart Rolland

Có an toàn để giả định rằng infiletồn tại trong thư mục hiện tại?
saiarcot895

Đúng. trong mã của tôi, nó là wget -O - thay vì một tệp. Trên thực tế, nếu tôi chỉ sao chép chuỗi nối và nối nó trong thiết bị đầu cuối thì nó chạy tốt
Lennart Rolland

Câu trả lời:


17

Tất cả phụ thuộc vào khi mọi thứ được đánh giá. Khi bạn nhập $cmd, toàn bộ phần còn lại của dòng được truyền dưới dạng đối số cho từ đầu tiên $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  

Điều này cho thấy các đối số được truyền cho echolệnh là: " /etc/passwd", " |" (ký tự thanh dọc), " wc" và " -l".

Từ 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

Một giải pháp cho vấn đề này, để tham khảo trong tương lai, là sử dụng "eval". Điều này đảm bảo rằng bất cứ cách nào chuỗi được giải thích bởi bash đều bị lãng quên và toàn bộ nội dung được đọc như thể nó được gõ trực tiếp vào một vỏ (đó chính xác là những gì chúng ta muốn).

Vì vậy, trong ví dụ trên, thay thế

$cmd

với

eval $cmd

đã giải quyết nó


Hãy cẩn thận với các thông số được trích dẫn. eval foo "a b"sẽ giống như eval foo "a" "b".
udondan

2

@waltinator đã giải thích lý do tại sao điều này không hoạt động như bạn mong đợi. Một cách khác là sử dụng bash -cđể thực thi lệnh của bạn:

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

1
Parsimony bảo tôi không bắt đầu một quy trình khác bash -c, nhưng sử dụng evalđể thực hiện lệnh trong quy trình hiện tại.
ví von

@waltinator chắc chắn, tôi cũng có thể sử dụng eval cho việc này (đó là lý do tại sao tôi nâng cấp bạn và Lennart). Tôi chỉ cung cấp một sự thay thế.
terdon

0

Có thể một cách tốt hơn để làm điều này là tránh sử dụng evalvà chỉ sử dụng một mảng Bash và đó là mở rộng nội tuyến để xây dựng tất cả các đối số và sau đó thực hiện chúng theo lệnh.

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.