Jakie są korzyści z używania $ () zamiast backticks w skryptach powłoki?

Istnieją dwa sposoby przechwytywania wyjścia z linii poleceń w bash:

  1. Legacy Bourne Shell backticks``:

     var=`command`
    
  2. $() składnia (która z tego co wiem jest specyficzna dla Basha)

     var=$(command)
    

Czy jest jakaś korzyść z używania drugiej składni w porównaniu do backticks? Czy te dwa są w pełni 100% równoważne?

Author: Breandán Dalton, 2012-02-26

8 answers

Główną z nich jest umiejętność zagnieżdżania się ich rozkazów, bez utraty rozsądku, próbując dowiedzieć się, czy jakaś forma ucieczki zadziała na backticach.

Przykład, choć nieco wymyślony:

deps=$(find /dir -name $(ls -1tr 201112[0-9][0-9]*.txt | tail -1l) -print)

Który wyświetli listę wszystkich plików w drzewie katalogów /dir, które mają taką samą nazwę jak najwcześniej datowany plik tekstowy z grudnia 2011 (a) .

Innym przykładem byłoby coś takiego jak zdobycie nazwy (nie pełnej ścieżka) katalogu nadrzędnego:

pax> cd /home/pax/xyzzy/plugh
pax> parent=$(basename $(dirname $PWD))
pax> echo $parent
xyzzy

(a) Teraz, gdy konkretne polecenie może nie działać, nie testowałem funkcjonalności. Więc, jeśli zagłosujesz na mnie na to, straciłeś z oczu zamiar : -) ma to być tylko ilustracja, jak można zagnieżdżać, a nie jako wolny od błędów fragment gotowy do produkcji.

 112
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-26 02:35:28

Załóżmy, że chcesz znaleźć katalog lib odpowiadający temu, gdzie jest zainstalowany gcc. Masz wybór:

libdir=$(dirname $(dirname $(which gcc)))/lib

libdir=`dirname \`dirname \\\`which gcc\\\`\``/lib

Pierwszy jest łatwiejszy niż drugi-użyj pierwszego.

 44
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-26 02:03:04

From man bash:

          $(command)
   or
          `command`

   Bash performs the expansion by executing command and replacing the com-
   mand  substitution  with  the  standard output of the command, with any
   trailing newlines deleted.  Embedded newlines are not deleted, but they
   may  be  removed during word splitting.  The command substitution $(cat
   file) can be replaced by the equivalent but faster $(< file).

   When the old-style backquote form of substitution  is  used,  backslash
   retains  its  literal  meaning except when followed by $, `, or \.  The
   first backquote not preceded by a backslash terminates the command sub-
   stitution.   When using the $(command) form, all characters between the
   parentheses make up the command; none are treated specially.
 21
Author: dgw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-26 01:41:59

Backticks (`...`) jest dziedziczoną składnią wymaganą tylko przez najstarszą z powłok bourne ' a, które nie są kompatybilne z POSIX, a $(...) jest preferowana z kilku powodów:

  • Ukośniki (\) wewnątrz backstic ' ów są obsługiwane w nieoczywisty sposób:

    $ echo "`echo \\a`" "$(echo \\a)"
    a \a
    $ echo "`echo \\\\a`" "$(echo \\\\a)"
    \a \\a
    # Note that this is true for *single quotes* too!
    $ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar" 
    foo is \, bar is \\
    
  • Zagnieżdżone cytowanie wewnątrz $() jest o wiele wygodniejsze:

    echo "x is $(sed ... <<<"$y")"
    

    Zamiast:

    echo "x is `sed ... <<<\"$y\"`"
    

    Lub pisząc coś w stylu:

    IPs_inna_string=`awk "/\`cat /etc/myname\`/"'{print $1}' /etc/hosts`
    

    Ponieważ $() używa zupełnie nowy kontekst dla cytowania

    Nie jest przenośny, ponieważ powłoki Bourne ' a i Korna wymagałyby tych ukośników, podczas gdy Bash i Dash Nie.]}
  • Składnia do zagnieżdżania podstawień poleceń jest łatwiejsza:

    x=$(grep "$(dirname "$path")" file)
    

    Niż:

    x=`grep "\`dirname \"$path\"\`" file`
    

    Ponieważ $() wymusza zupełnie nowy kontekst dla cytowania, więc każde zastępowanie poleceń jest chronione i może być traktowane samodzielnie bez szczególnej troski o cytowanie i ucieczki. Kiedy używasz backsticków, staje się brzydszy i brzydszy po dwóch i wyżej poziomach.

    Kilka przykładów:

    echo `echo `ls``      # INCORRECT
    echo `echo \`ls\``    # CORRECT
    echo $(echo $(ls))    # CORRECT
    
  • Rozwiązuje problem niespójnego zachowania podczas używania backquotes:]}
    • echo '\$x' wyjścia \$x
    • echo `echo '\$x'` wyjścia $x
    • echo $(echo '\$x') wyjścia \$x
  • Składnia

    Backticks ma historyczne ograniczenia dotyczące zawartości osadzonego polecenia i nie może obsługiwać niektórych ważnych skryptów zawierających backquotes, podczas gdy nowsza forma $() może przetwarzać każdy rodzaj poprawnego wbudowanego skryptu.

    Na przykład, te poprawne wbudowane skrypty nie działają w lewej kolumnie, ale działają po prawej stronieIEEE:

    echo `                         echo $(
    cat <<\eof                     cat <<\eof
    a here-doc with `              a here-doc with )
    eof                            eof
    `                              )
    
    
    echo `                         echo $(
    echo abc # a comment with `    echo abc # a comment with )
    `                              )
    
    
    echo `                         echo $(
    echo '`'                       echo ')'
    `                              )
    

Dlatego składnia dla $-prefiks zastąpienie polecenia powinna być preferowaną metodą, ponieważ jest wizualnie przejrzysta z czystą składnią( poprawia czytelność człowieka i maszyny), jest zagnieżdżalna i intuicyjna, jej wewnętrzne parsowanie jest oddzielne i jest jest również bardziej spójny (ze wszystkimi innymi rozszerzeniami, które są przetwarzane z cudzysłowów), gdzie jedynym wyjątkiem są backticki, a znak ` jest łatwo zakamuflowany, gdy sąsiaduje z ", co czyni go jeszcze trudniejszym do odczytania, szczególnie przy małych lub nietypowych czcionkach.

Source: Why is $(...) preferred over `...` (backticks)? at BashFAQ

Zobacz też:

 19
Author: kenorb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-23 11:36:50

Oprócz innych odpowiedzi,

$(...)

Wyróżnia się wizualnie lepiej niż

`...`

Backticki wyglądają zbyt podobnie do apostrofów; różni się to w zależności od używanej czcionki.

(i, jak właśnie zauważyłem, backticki są dużo trudniejsze do wprowadzenia w próbkach kodu inline.)

 9
Author: Keith Thompson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-26 01:58:27

$() umożliwia zagnieżdżanie.

out=$(echo today is $(date))
Myślę, że backticks na to nie pozwala.
 6
Author: Shiplu Mokaddim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-26 01:44:03

Jest to standard POSIX, który definiuje $(command) formę zastępowania poleceń. Większość obecnie używanych powłok jest zgodna ze standardem POSIX i obsługuje tę preferowaną formę w stosunku do archaicznej notacji backtick. Sekcja (2.6.3) zastępowanie poleceń dokumentu języka powłoki opisuje to:

Zastępowanie poleceń pozwala na podstawianie wyjścia polecenia zamiast samej nazwy polecenia.  Zastąpienie polecenia następuje, gdy polecenie jest załączone jako następuje:

$(command)

Or (backquoted version):

`command`

Powłoka rozszerza podstawienie polecenia wykonując polecenie w środowisku subshell (zobacz Shell Execution Environment ) i zastępowanie polecenia substytucja (tekst polecenia plus załączając "$ () " lub backquotes) ze standardowym wyjściem polecenie, usuwanie sekwencji jednego lub więcej <newline> znaków w koniec zastępstwo. Osadzone <newline> znaki przed końcem wyjścia nie mogą być usuwane, jednak mogą być traktowane jako ograniczniki pola i eliminowane podczas podziału pola, w zależności od wartość Fi i cytowania, które są w mocy. Jeśli wyjście zawiera dowolne bajty null, zachowanie jest nieokreślone.

Wewnątrz cytowanego stylu zastępowania poleceń, <backslash> zachowuje swoje dosłowne znaczenie, chyba że po: '$' , '`', lub <backslash>. The search for pasujący cytat powinien być spełniony przez pierwszy nie cytowany, nieusuwalny backquote; podczas tego Wyszukiwania, jeśli nieusuwalny backquote jest spotykany w komentarzu powłoki, a tutaj-document, osadzone polecenie zastępujące polecenie $( ) w postaci lub cytowanym łańcuchu występują niezdefiniowane wyniki. Jednocyfrowy lub podwójny cytowany ciąg, który zaczyna się, ale nie kończy, w obrębie " `...`" Sekwencja daje nieokreślone wyniki.

Z poleceniem $( ) w postaci, wszystkie znaki po otwartym nawias do pasującego nawiasu zamykającego tworzą polecenie . Każdy poprawny skrypt powłoki może być użyty do polecenia , Z wyjątkiem skrypt składający się wyłącznie z przekierowań, które generują nieokreślone wyniki.

Wyniki zastępowania poleceń nie będą przetwarzane do dalszych Rozszerzanie tyldy, Rozszerzanie parametrów, zastępowanie poleceń lub ekspansja arytmetyczna. Jeśli zachodzi Zamiana polecenia wewnątrz podwójne cudzysłowy, dzielenie pól i rozszerzanie nazw ścieżek nie mogą być wykonywane na podstawie wyników substytucji.

Zastępowanie poleceń może być zagnieżdżone. Aby określić zagnieżdżanie w obrębie wersja backquoted, aplikacja powinna poprzedzać wewnętrzne backquotes ze znakami <backslash>; na przykład:

\`command\`

Składnia języka poleceń powłoki ma niejednoznaczność dla rozszerzeń zaczynających się od "$((", które mogą wprowadzić rozszerzenie arytmetyczne lub zastępowanie poleceń, które zaczyna się od podshell. Ekspansja arytmetyczna ma pierwszeństwo, czyli powłoka najpierw wyznacza czy może analizować ekspansję jako ekspansję arytmetyczną i analizuje tylko rozszerzenie jako substytucję polecenia jeśli stwierdzi, że nie może przetworzyć rozszerzenia jako rozszerzenia arytmetycznego. Powłoka nie musi oceniać zagnieżdżonych rozszerzeń podczas wykonywania tego określenia. Jeśli napotka koniec wejścia bez określenia że nie może analizować ekspansji jako ekspansji arytmetycznej, powłoka traktuje rozszerzenie jako niekompletne rozszerzenie arytmetyczne i zgłasza błąd składni. Zgodny wniosek powinien zapewnić oddzielenie" $( " i "( " na dwa żetony (to znaczy oddzielić je spacjami) w podstawianiu poleceń, które zaczyna się od podshell. Na przykład, podstawienie polecenia zawierające pojedynczą podshell może być zapisane jako:

$( (command) )

 2
Author: JRFerguson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-21 00:06:05

To jest pytanie dziedziczne, ale wymyśliłem doskonale poprawny przykład $(...) nad `...`.

Używałem zdalnego pulpitu do windows z cygwin i chciałem iterację na wynik polecenia. Niestety, postać backtic ' a nie była możliwa do wprowadzenia, zarówno ze względu na zdalny pulpit, jak i sam cygwin.

Rozsądnie jest założyć, że znak dolara i nawiasy będą łatwiejsze do wpisania w tak dziwnych Ustawieniach.

 2
Author: Piotr Zierhoffer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-04 11:10:48