Jaka jest różnica między „eval” a „source / dev / stdin”?


17

Pomiędzy następującymi alternatywami ...

  1. z eval.

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

    printf "ls" | source /dev/stdin
  3. z source /dev/stdini ( )lub{ }

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

    (Gdy prowadzimy printfw { }, jest tam żadnych korzyści inne niż nie używając powłoki w tle?)

    • Jaka jest różnica między nimi?

    • Który jest preferowany?

    • Jaki jest preferowany sposób uruchamiania poleceń? ()czy {}?


1
Nie poleciłbym żadnego z tych podejść. Co tak naprawdę próbujesz zrobić , że Twoim zdaniem musisz wykonać dowolny kod przesłany przez użytkownika?
chepner

2
Wydawało mi się również, że dokonywali dowolnych danych wejściowych użytkownika (tak jak jest), dopóki nie przeczytałem pytania. Ale być może przewidujesz, że tak będzie.
ctrl-alt-delor

Odpowiedzi:


17
  • Jaka jest różnica między sposobami?

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

Nie ma różnic między tymi dwoma sposobami.

Jest tylko jedna uwaga: evalłączy wszystkie argumenty, które są następnie uruchamiane jako pojedyncze polecenie. sourceodczytuje zawartość pliku i wykonuje je. evalmoże budować polecenia tylko na podstawie swoich argumentów, a nie stdin. Więc nie możesz tego zrobić:

printf "ls" | eval
  • Który jest bardziej preferowany?

Twój przykład zapewnia ten sam wynik, ale cel evali sourcejest inny. sourcejest zwykle używany do udostępniania biblioteki dla innych skryptów, natomiast evalsłuży wyłącznie do oceny poleceń. Jeśli evalto możliwe, należy unikać używania , ponieważ nie ma gwarancji, że ewaluowany ciąg jest czysty; subshellzamiast tego musimy przeprowadzić kontrolę poczytalności .

  • Jeśli uruchomimy niektóre polecenia w () lub {}, co jest bardziej preferowane?

Kiedy uruchamiasz polecenia sekwencji w nawiasach klamrowych { }, wszystkie polecenia są uruchamiane w bieżącej powłoce , a nie w podpowłoce (tak jest w przypadku uruchamiania w nawiasach (patrz odniesienie do bash )).

Korzystanie subshell ( )zużywa więcej zasobów, ale nie wpływa to na bieżące środowisko. Użycie { }uruchamia wszystkie polecenia w bieżącej powłoce, więc wpływa to na środowisko. W zależności od celu możesz wybrać jeden z nich.


2
Myślę, że źle zrozumiałeś pytanie. Oczywiście, nie można zastąpić evalprzez source. Myślę, że pytanie brzmi: jest eval "$cmd"równoważne echo "$cmd" | source /dev/stdin. Moja obecna opinia brzmi: tak.
Hauke ​​Laging

3

Główną różnicą jest to, że druga i trzecia forma używają potoku, który zmusi bash do uruchomienia polecenia „source” w podpowłoce (chyba że ustawiona jest ostatnia rura, dostępna tylko w bash 4.2+), co sprawi, że będzie ona prawie odpowiednikiem :

printf "ls" | bash

Konsekwencje są takie, że wszelkie zmienne środowiskowe ustawione przez kod zostaną utracone, więc nie będzie działać zgodnie z oczekiwaniami:

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

Aby uruchomić polecenia w bieżącej powłoce, możesz użyć podstawienia procesu:

source <(printf "abc=2")

Możesz umieścić więcej poleceń w nawiasach, używając zwykłego średnika.

Jeśli wyeliminujesz potok w ten sposób, uważam, że nie ma różnicy między użyciem „eval” i „source”. Powinieneś wybrać ten, który jest łatwiejszy w użyciu w konkretnym przypadku:

  • jeśli masz już komendy do działania w zmiennej, użyj „eval”
  • jeśli masz je w pliku lub pobierasz z zewnętrznego polecenia, użyj „źródła”

0

Jako uzupełnienie już udzielonych odpowiedzi:

sourceOdpowiednik ...

comd="ls"
eval "$comd"

... jest ...

source <(printf ls)

W przypadku lsnie ma znaczącej różnicy.

Ale w przypadku polecenia, które ma wpłynąć na twoje obecne środowisko (co zwykle zamierzasz podczas używania source), ten wariant zrobiłby to (podobnie jak twoje pierwsze rozwiązanie eval), podczas gdy twoje drugie podejście tylko wpływa na środowisko podpowłoki, która wygrała będą dostępne po wykonaniu linii kodu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.