Apa perbedaan antara "eval" dan "source / dev / stdin"?


17

Di antara alternatif berikut ...

  1. dengan eval.

    comd="ls"
    eval "$comd"
  2. dengan source /dev/stdin

    printf "ls" | source /dev/stdin
  3. dengan source /dev/stdindan ( )atau{ }

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

    (Ketika kita menjalankan printfdi { }, apakah ada manfaat lain selain tidak menggunakan subkulit?)

    • Apa perbedaan di antara mereka?

    • Mana yang lebih disukai?

    • Mana cara yang disukai untuk menjalankan perintah? ()atau {}?


1
Saya tidak akan merekomendasikan pendekatan mana pun. Apa yang sebenarnya Anda coba lakukan , yang menurut Anda perlu mengeksekusi kode arbitrer yang dikirimkan oleh pengguna?
chepner

2
Saya juga meskipun mereka mengeksekusi input pengguna yang sewenang-wenang (sebagaimana adanya), sampai saya membaca pertanyaan. Tetapi mungkin Anda memprediksi bahwa mereka akan melakukannya.
ctrl-alt-delor

Jawaban:


17
  • Apa perbedaan antara cara-cara itu?

dari 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.

Tidak ada perbedaan antara dua cara.

Hanya ada satu catatan: evalmenyatukan semua argumennya, yang kemudian dijalankan sebagai satu perintah. sourcemembaca isi file dan mengeksekusinya. evalhanya dapat membangun perintah dari argumennya, bukan stdin. Jadi Anda tidak bisa melakukan seperti ini:

printf "ls" | eval
  • Mana yang lebih disukai?

Contoh Anda memberikan hasil yang sama, tetapi tujuan evaldan sourceberbeda. sourcebiasanya digunakan untuk menyediakan pustaka untuk skrip lain, sementara evalhanya digunakan untuk mengevaluasi perintah. Anda harus menghindari penggunaan evaljika memungkinkan, karena tidak ada jaminan bahwa string yang diaktifkan bersih; kita harus melakukan beberapa pemeriksaan kewarasan, subshellsebagai gantinya.

  • Jika kita menjalankan beberapa perintah di () atau {}, mana yang lebih disukai?

Saat Anda menjalankan perintah sekuens di dalam kurung kurawal { }, semua perintah dijalankan di shell saat ini , alih-alih sebuah subkulit (yang merupakan kasus jika Anda menjalankan di dalam tanda kurung (lihat referensi bash )).

Menggunakan subshell ( )menggunakan lebih banyak sumber daya, tetapi lingkungan Anda saat ini tidak terpengaruh. Menggunakan { }menjalankan semua perintah di shell saat ini, sehingga lingkungan Anda terpengaruh. Tergantung pada tujuan Anda, Anda dapat memilih salah satunya.


2
Saya pikir Anda salah paham pertanyaannya. Tentu saja, Anda tidak dapat menggantinya evaldengan source. Saya kira pertanyaannya adalah: eval "$cmd"Setara dengan echo "$cmd" | source /dev/stdin. Pendapat saya saat ini adalah: ya.
Hauke ​​Laging

3

Perbedaan utama adalah bahwa formulir ke-2 dan ke-3 menggunakan pipa, yang akan memaksa bash untuk menjalankan perintah "sumber" dalam sebuah subkulit (kecuali jika lastpipe diatur, hanya tersedia dalam bash 4.2+), yang akan membuatnya cukup setara dengan :

printf "ls" | bash

Konsekuensinya adalah bahwa setiap variabel lingkungan yang ditetapkan oleh kode Anda akan hilang, jadi ini tidak akan berfungsi seperti yang diharapkan:

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

Untuk menjalankan perintah di shell saat ini, Anda dapat menggunakan substitusi proses:

source <(printf "abc=2")

Anda dapat menempatkan lebih banyak perintah di dalam tanda kurung menggunakan titik koma seperti biasa.

Jika Anda menghilangkan pipa dengan cara ini, saya percaya tidak ada perbedaan antara menggunakan "eval" dan "source". Anda harus memilih yang lebih mudah digunakan dalam kasus khusus Anda:

  • jika Anda sudah memiliki perintah untuk dijalankan dalam variabel, gunakan "eval"
  • jika Anda memilikinya dalam file atau mendapatkannya dari perintah eksternal, gunakan "sumber"

0

Sebagai pelengkap jawaban yang sudah diberikan:

Sebuah sourcesetara dengan ...

comd="ls"
eval "$comd"

... adalah ...

source <(printf ls)

Dalam hal lstidak ada perbedaan yang signifikan.

Tetapi dalam kasus perintah yang dimaksudkan untuk mempengaruhi lingkungan Anda saat ini (apa yang biasanya Anda maksudkan saat menggunakan source) varian ini akan melakukannya (seperti solusi pertama Anda evaljuga akan) sementara pendekatan kedua Anda hanya mempengaruhi lingkungan subkulit yang menang ' t tersedia setelah mengeksekusi baris kode Anda.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.