อะไรคือความแตกต่างระหว่าง“ eval” และ“ source / dev / stdin”?


17

ระหว่างทางเลือกต่อไปนี้ ...

  1. evalกับ

    comd="ls"
    eval "$comd"
  2. กับ source /dev/stdin

    printf "ls" | source /dev/stdin
  3. ด้วยsource /dev/stdinและ( )หรือ{ }

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

    (เมื่อเราทำงานprintfใน{ }จะมีผลประโยชน์อื่น ๆ กว่าไม่ได้ใช้ subshell ใด ๆ ?)

    • ความแตกต่างระหว่างพวกเขาคืออะไร?

    • ข้อไหนดี?

    • วิธีใดในการรันคำสั่งที่ต้องการ ()หรือ{}?


1
ฉันจะไม่แนะนำวิธีการอย่างใดอย่างหนึ่ง คุณพยายามทำอะไรจริงๆคุณคิดว่าคุณต้องใช้รหัสโดยอำเภอใจที่ผู้ใช้ส่งมา?
chepner

2
ฉันยังว่าพวกเขากำลังดำเนินการป้อนข้อมูลของผู้ใช้โดยพลการ (เช่นเดิม) จนกว่าฉันจะอ่านคำถาม แต่อาจเป็นคุณกำลังทำนายว่าพวกเขาจะ
ctrl-alt-delor

คำตอบ:


17
  • ความแตกต่างระหว่างวิธีคืออะไร?

จาก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.

ไม่มีความแตกต่างระหว่างสองวิธี

มีบันทึกย่อเดียวเท่านั้น: evalเชื่อมอาร์กิวเมนต์ทั้งหมดที่มีอยู่แล้วซึ่งเรียกใช้เป็นคำสั่งเดียว sourceอ่านเนื้อหาของไฟล์และดำเนินการ evalสามารถสร้างคำสั่งจากอาร์กิวเมนต์ได้stdinเท่านั้น ดังนั้นคุณไม่สามารถทำสิ่งนี้:

printf "ls" | eval
  • ข้อไหนที่ต้องการมากกว่า

ตัวอย่างของคุณให้ผลลัพธ์เดียวกัน แต่จุดประสงค์evalและsourceแตกต่างกัน sourceมักจะใช้สำหรับการจัดทำไลบรารีสำหรับสคริปต์อื่น ๆ ในขณะที่evalใช้เพื่อประเมินคำสั่งเท่านั้น คุณควรหลีกเลี่ยงการใช้evalถ้าเป็นไปได้เพราะไม่มีการรับประกันว่าสายอักขระที่ Evaled นั้นสะอาด เราต้องทำการตรวจสอบสติบางอย่างโดยใช้subshellแทน

  • หากเราเรียกใช้คำสั่งบางอย่างใน () หรือ {} ซึ่งเป็นที่ต้องการมากกว่า

เมื่อคุณรันคำสั่งลำดับภายในวงเล็บปีกกา{ }คำสั่งทั้งหมดจะถูกเรียกใช้ในเชลล์ปัจจุบันแทนที่จะเป็นเชลล์ย่อย (ซึ่งเป็นกรณีถ้าคุณเรียกใช้ภายในวงเล็บ (ดูการอ้างอิง bash ))

การsubshell ( )ใช้ทรัพยากรมากขึ้น แต่สภาพแวดล้อมปัจจุบันของคุณจะไม่ได้รับผลกระทบ การใช้{ }รันคำสั่งทั้งหมดในเชลล์ปัจจุบันดังนั้นสภาวะแวดล้อมของคุณจะได้รับผลกระทบ คุณสามารถเลือกหนึ่งในนั้นขึ้นอยู่กับวัตถุประสงค์ของคุณ


2
ฉันคิดว่าคุณเข้าใจผิดคำถาม แน่นอนคุณไม่สามารถแทนที่โดยeval sourceผมคิดว่าคำถามคือคือเทียบเท่ากับeval "$cmd" echo "$cmd" | source /dev/stdinความเห็นปัจจุบันของฉันคือ: ใช่
Hauke ​​Laging

3

ข้อแตกต่างที่สำคัญคือรูปแบบที่ 2 และ 3 กำลังใช้ไพพ์ซึ่งจะบังคับให้ bash รันคำสั่ง "source" ใน subshell (ยกเว้นว่าตั้งค่า Lastpipe ให้ใช้ใน bash 4.2+ เท่านั้น) ซึ่งจะทำให้มันค่อนข้างเทียบเท่ากับ :

printf "ls" | bash

ผลที่ตามมาคือตัวแปรสภาพแวดล้อมใด ๆ ที่รหัสของคุณตั้งค่าไว้จะสูญหายดังนั้นสิ่งนี้จะไม่ทำงานตามที่คาดไว้:

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

ในการรันคำสั่งในเชลล์ปัจจุบันคุณสามารถใช้การทดแทนกระบวนการ:

source <(printf "abc=2")

คุณสามารถใส่คำสั่งเพิ่มเติมในวงเล็บโดยใช้เครื่องหมายอัฒภาคได้ตามปกติ

หากคุณกำจัดท่อด้วยวิธีนี้ฉันเชื่อว่าไม่มีความแตกต่างระหว่างการใช้ "eval" และ "แหล่งที่มา" คุณควรเลือกใช้ที่ง่ายกว่าในกรณีของคุณโดยเฉพาะ:

  • หากคุณมีคำสั่งให้ทำงานในตัวแปรอยู่แล้วให้ใช้ "eval"
  • หากคุณมีพวกเขาในไฟล์หรือรับพวกเขาจากคำสั่งภายนอกใช้ "แหล่งที่มา"

0

เป็นส่วนประกอบของคำตอบที่ได้รับไปแล้ว:

sourceเทียบเท่ากับ ...

comd="ls"
eval "$comd"

... คือ ...

source <(printf ls)

ในกรณีที่ lsไม่มีความแตกต่างอย่างมีนัยสำคัญ

แต่ในกรณีของคำสั่งที่มีวัตถุประสงค์เพื่อส่งผลกระทบต่อสภาพแวดล้อมปัจจุบันของคุณ (สิ่งที่คุณมักจะตั้งใจเมื่อใช้source) ตัวแปรนี้จะทำ (เป็นวิธีแก้ปัญหาที่ 1 ของคุณด้วยeval) ในขณะที่วิธีที่ 2 ของคุณส่งผลกระทบต่อสภาพแวดล้อมของ subshell ไม่สามารถใช้งานได้หลังจากรันโค้ดของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.