Thực hiện một lệnh được lưu trữ trong một biến


11

Tôi có một lệnh được lưu trữ trong một biến. Hãy giả vờ biến $icó giá trị:

cat -nT index.php |grep 'someregex'

Khi tôi cố gắng thực hiện biến trên bằng cách gõ $inó không thành công vì shell cố thực thi toàn bộ biến dưới dạng một lệnh. Tôi cũng đã thử sử dụng eval($i)và đưa $ivào backticks.

Làm thế nào tôi có thể làm cho shell thực thi $inhư thể nó là một lệnh? Và tại sao nó không hoạt động giống như.

$i='echo hi'; $i

Có phải vì tôi đã phải sắp xếp hack trong dấu ngoặc đơn? (Bởi vì bạn không thể lồng chúng.) Hiện tại giải pháp của tôi là

echo $i > /foo;  . /foo

Nhưng tôi không muốn tạo một tệp chỉ cho việc này, chỉ để xóa nó sau.

Ý tôi là "Tôi đã hack trong các trích dẫn duy nhất là tôi đã làm:

$i='cat index.php | grep -P '"'"'MYREGEXHERE'"'"

4
Câu hỏi là, tại sao lệnh được lưu trữ trong một biến ở vị trí đầu tiên? Tốt hơn hết là bạn nên lắp ráp lệnh khi bạn chạy nó, lưu trữ các đối số tùy chọn trong các biến hoặc trong một mảng. Bạn có thể hiển thị toàn bộ kịch bản bạn có?
slhck

Nguyên nhân gốc rễ: superuser.com/questions/360966/
Mạnh

Câu trả lời:


18

Câu trả lời ngắn: xem BashAQ # 50: Tôi đang cố gắng đặt một lệnh vào một biến, nhưng các trường hợp phức tạp luôn thất bại! .

Câu trả lời dài: đó là do thứ tự bash phân tích dòng lệnh. Cụ thể, nó tìm kiếm những thứ như đường ống và chuyển hướng trước khi nó mở rộng các giá trị biến đổi, và không quay lại và tìm kiếm lại các đường ống, v.v. trong các giá trị mở rộng. Về cơ bản, các biến được thay thế khoảng một nửa trong quá trình phân tích cú pháp, do đó, giá trị chỉ được phân tách một nửa trước khi được thực thi.

Để giải quyết vấn đề này, bạn cần trả lời câu hỏi của @ slhck: tại sao lệnh được lưu trữ trong một biến ở vị trí đầu tiên? Vấn đề thực tế bạn đang cố gắng giải quyết là gì? Tùy thuộc vào mục tiêu thực tế, có một số giải pháp có thể:

  • Đừng lưu nó trong một biến, chỉ cần thực hiện trực tiếp. Lưu trữ các lệnh để sử dụng sau này là khó khăn và nếu bạn không thực sự cần, thì đừng.

  • Sử dụng một hàm thay vì một biến. Rốt cuộc, đó là những gì họ dành cho:

    i() { cat -nT index.php |grep 'someregex'; }
    i

    Nhược điểm chính của điều này là bạn không thể tạo hàm một cách linh hoạt - bạn không thể bao gồm hoặc loại trừ mã khỏi hàm một cách có điều kiện (mặc dù chính hàm có thể có các phần tử có điều kiện được chọn khi nó được thực thi).

  • Sử dụng eval. Đây nên được coi là phương sách cuối cùng, vì nó dễ dàng có hành vi bất ngờ. Về cơ bản, nó chạy lệnh thông qua một phân tích cú pháp phân tích đầy đủ khác, vì vậy tất cả các ống vv đều có ý nghĩa đầy đủ của chúng - nhưng điều đó cũng có nghĩa là các phần của lệnh bạn nghĩ chỉ là dữ liệu cũng sẽ được phân tích cú pháp và có thể được thực thi. Tên tệp có chứa siêu ký tự vỏ (ống, dấu chấm phẩy, trích dẫn / dấu nháy đơn, v.v.) có thể có tác dụng kỳ lạ và đôi khi nguy hiểm. Nếu bạn sử dụng eval, ít nhất là trích dẫn hai chuỗi, nếu không, nội dung của chuỗi về cơ bản sẽ được phân tích cú pháp một lần rưỡi, với kết quả thậm chí kỳ lạ hơn.

    i="cat -nT index.php |grep 'someregex'"
    eval "$i"

EDIT: Một cách tiếp cận cửa hàng-a-lệnh-in-a-biến tiêu chuẩn khác là sử dụng một mảng thay vì một biến đơn giản. Điều này cho phép bạn lưu trữ một lệnh với các đối số phức tạp (ví dụ như chứa khoảng trắng) mà không gặp sự cố, nhưng sẽ không lưu trữ những thứ như đường ống và chuyển hướng. Vì vậy, cách tiếp cận mảng sẽ không hữu ích trong trường hợp cụ thể này.


+1 cho evalphương thức. Điều này ngăn tôi hỏi cùng một câu hỏi :). Tôi có một cái gì đó như thế sudo rsync -aAHXi -n --delete-excluded --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/lost+found","/mnt/DATA/*","/var/log/*","/var/swap","/var/cache/apt/archives/*.deb"} -e ssh root@piac_wireless:/ /home/mrx/Docs/RPi/backup/piac/piac_usb-rootkhông thực hiện chính xác các loại trừ.
vết lõm

@dentex Tôi thực sự khuyên bạn không nên sử dụng evalcho việc này - có quá nhiều cách để nó sai. Một mảng sẽ là một cách tốt hơn để làm điều này. Xem ở đây , ở đây , và ở đây cho ví dụ.
Gordon Davisson

Cám ơn vì sự gợi ý. Tôi sẽ cố gắng thực hiện giải pháp với mảng, để chuyển danh sách loại trừ sang rsync.
vết lõm

4

Điều này chỉ nên làm việc:

a="cat -nT index.php |grep 'someregex'"
eval "$a"

Cẩn thận evalgiới thiệu các lỗ hổng tiềm năng và các tình huống hành vi bất ngờ vì vậy phải được sử dụng, nếu có, với sự cẩn thận thêm.


Nếu bạn sử dụng eval, bạn thực sự cần sử dụng dấu ngoặc kép ( eval "$i") để tránh các hiệu ứng cực kỳ lạ. Ví dụ: thử điều này với a="cat -nT index.php |grep ' .* '"(tức là regex phải khớp hai khoảng trắng với bất kỳ chuỗi ký tự nào giữa chúng) và xem điều gì thực sự xảy ra. Đối với tín dụng thêm, giải thích tại sao nó xảy ra.
Gordon Davisson

@GordonDavisson báo giá kép được thêm vào.
jlliagre

2

Khi khai báo biến, bạn không nên sử dụng ký hiệu đô la làm tiền tố.

Thử cái này:

i='echo hi'; $i

1
Điều đó đúng, nhưng thực sự đó không phải là câu trả lời cho câu hỏi của OP.
slhck

đối với tôi
THỰC SỰ
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.