Sự khác biệt giữa nguồn của ev eval và nguồn / dev / stdin là gì?


17

Giữa các lựa chọn sau ...

  1. với eval.

    comd="ls"
    eval "$comd"
  2. với source /dev/stdin

    printf "ls" | source /dev/stdin
  3. với source /dev/stdin( )hoặc{ }

    ( printf "ls" ) | source /dev/stdin
    { printf "ls"; } | source /dev/stdin

    (Khi chúng tôi chạy printfvào { }, có lợi ích nào khác ngoài việc không sử dụng subshell không?)

    • sự khác biệt giữa chúng là gì?

    • Cái nào được ưa thích?

    • Đó là cách ưa thích để chạy lệnh? ()hay {}?


1
Tôi sẽ không đề nghị một trong hai cách tiếp cận. Bạn thực sự đang cố gắng làm gì , mà bạn nghĩ rằng bạn cần phải thực thi mã tùy ý được gửi bởi người dùng?
chepner

2
Tôi cũng mặc dù họ đang thực hiện đầu vào người dùng tùy ý (như vậy), cho đến khi tôi đọc câu hỏi. Nhưng có thể bạn đang dự đoán rằng họ sẽ.
ctrl-alt-delor

Câu trả lời:


17
  • Sự khác biệt giữa các cách là gì?

từ bash manpage:

eval [arg ...]
              The  args  are read and concatenated together into a single com
              mand.  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.

source filename [arguments]
              Read and execute commands from filename  in  the  current  shell
              environment  and return the exit status of the last command exe
              cuted from filename.  If filename does not contain a slash, file
              names  in  PATH  are used to find the directory containing file
              name.  The file searched for in PATH  need  not  be  executable.
              When  bash  is  not  in  posix  mode,  the  current directory is
              searched if no file is found in PATH.  If the sourcepath  option
              to  the  shopt  builtin  command  is turned off, the PATH is not
              searched.  If any arguments are supplied, they become the  posi
              tional  parameters  when  filename  is  executed.  Otherwise the
              positional parameters are unchanged.  The return status  is  the
              status  of  the  last  command exited within the script (0 if no
              commands are executed), and false if filename is  not  found  or
              cannot be read.

Không có sự khác biệt giữa hai cách.

Chỉ có một lưu ý: evalnối tất cả các đối số của nó, sau đó được chạy dưới dạng một lệnh duy nhất. sourceđọc nội dung của tập tin và thực thi chúng evalChỉ có thể xây dựng các lệnh từ các đối số của nó, không stdin. Vì vậy, bạn không thể làm như thế này:

printf "ls" | eval
  • Cái nào được ưa thích hơn?

Ví dụ của bạn cung cấp cùng một kết quả, nhưng mục đích evalsourcekhác nhau. sourcethường được sử dụng để cung cấp một thư viện cho các tập lệnh khác, trong khi evalchỉ được sử dụng để đánh giá các lệnh. Bạn nên tránh sử dụng evalnếu có thể, bởi vì không có gì đảm bảo rằng chuỗi bị loại bỏ là sạch; chúng ta phải làm một số kiểm tra vệ sinh, sử dụng subshellthay thế.

  • Nếu chúng ta chạy một số lệnh trong () hoặc {}, cái nào được ưa thích hơn?

Khi bạn chạy các lệnh tuần tự bên trong dấu ngoặc nhọn { }, tất cả các lệnh được chạy trong trình bao hiện tại , thay vì một lớp con (đó là trường hợp nếu bạn chạy bên trong dấu ngoặc đơn (xem tham chiếu bash )).

Sử subshell ( )dụng sử dụng nhiều tài nguyên hơn, nhưng môi trường hiện tại của bạn không bị ảnh hưởng. Sử dụng { }chạy tất cả các lệnh trong shell hiện tại, vì vậy môi trường của bạn bị ảnh hưởng. Tùy thuộc vào mục đích của bạn, bạn có thể chọn một trong số họ.


2
Tôi nghĩ rằng bạn đã hiểu nhầm câu hỏi. Tất nhiên, bạn không thể thay thế evalbằng source. Tôi đoán câu hỏi là: Có eval "$cmd"tương đương với echo "$cmd" | source /dev/stdin. Ý kiến ​​hiện tại của tôi là: có.
Hauke ​​Laging

3

Sự khác biệt chính là dạng thứ 2 và thứ 3 đang sử dụng một đường ống, điều này sẽ buộc bash chạy lệnh "nguồn" trong một khung con (trừ khi cài đặt Lastpipe, chỉ có sẵn trong bash 4.2+), điều này sẽ khiến nó tương đương với :

printf "ls" | bash

Hậu quả là bất kỳ biến môi trường nào được đặt bởi mã của bạn sẽ bị mất, do đó, điều này sẽ không hoạt động như mong đợi:

printf "abc=2" | source /dev/stdin

Để chạy các lệnh trong shell hiện tại, bạn có thể sử dụng quy trình thay thế:

source <(printf "abc=2")

Bạn có thể đặt nhiều lệnh hơn trong ngoặc đơn bằng dấu chấm phẩy như bình thường.

Nếu bạn loại bỏ đường ống theo cách này, tôi tin rằng không có sự khác biệt giữa việc sử dụng "eval" và "nguồn". Bạn nên chọn cái đơn giản hơn để sử dụng trong trường hợp cụ thể của bạn:

  • nếu bạn đã có các lệnh để chạy trong biến, hãy sử dụng "eval"
  • nếu bạn có chúng trong một tệp hoặc nhận chúng từ lệnh bên ngoài, hãy sử dụng "nguồn"

0

Như một bổ sung cho các câu trả lời đã được đưa ra:

Một sourcetương đương với ...

comd="ls"
eval "$comd"

... Là ...

source <(printf ls)

Trong trường hợp lskhông có sự khác biệt đáng kể.

Nhưng trong trường hợp lệnh có ý định ảnh hưởng đến môi trường hiện tại của bạn (những gì bạn thường dự định khi sử dụng source) thì biến thể này sẽ thực hiện (như giải pháp đầu tiên của bạn evalcũng sẽ) trong khi cách tiếp cận thứ 2 của bạn chỉ ảnh hưởng đến môi trường của một mạng con đã thắng ' t có sẵn sau khi thực hiện dòng mã của bạn.

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.