Qual è il vantaggio dell’uso di $ () invece dei backtick negli script di shell?

Esistono due modi per acquisire l’output della riga di comando in bash :

  1. I backtick della shell di Bourne legacy `` :

      var=`command` 
  2. $() syntax (che per quanto ne so è Bash specifico)

      var=$(command) 

C’è qualche vantaggio nell’usare la seconda syntax rispetto ai backtick? O i due sono completamente equivalenti al 100%?

Il più importante è la possibilità di annidarli , i comandi all’interno dei comandi, senza perdere la sanità mentale cercando di capire se qualche forma di fuga funzionerà sui backtick.

Un esempio, anche se un po ‘forzato:

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

che fornirà un elenco di tutti i file nella struttura della directory /dir che hanno lo stesso nome del file di testo datato meno recente di dicembre 2011 (a) .

Un altro esempio potrebbe essere qualcosa come ottenere il nome (non il percorso completo) della directory padre:

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

(a) Ora che il comando specifico potrebbe non funzionare, non ho testato la funzionalità. Quindi, se mi votate per questo, avete perso di vista l’intento 🙂 È solo un esempio di come potete nidificare, non come uno snippet pronto per la produzione privo di bug.

Supponiamo di voler trovare la directory lib corrispondente a dove gcc è installato. Hai una scelta:

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

Il primo è più facile del secondo: usa il primo.

Da uomo 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. 

I backtick ( `...` ) sono la syntax legacy richiesta solo dal più vecchio dei bourne-shell non POSIX-compatibili e $(...) è POSIX e più preferito per diversi motivi:

  • Le barre rovesciate ( \ ) all’interno dei backtick sono gestite in modo non ovvio:

     $ 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 \\ 
  • La citazione annidata in $() è molto più comoda:

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

    invece di:

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

    o scrivere qualcosa come:

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

    perché $() utilizza un contesto completamente nuovo per la citazione

    che non è portatile in quanto le shell Bourne e Korn richiedono queste barre rovesciate, mentre Bash e dash non lo fanno.

  • La syntax per le sostituzioni dei comandi di nidificazione è più semplice:

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

    di:

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

    poiché $() applica un contesto completamente nuovo per la citazione, quindi ogni sostituzione di comando è protetta e può essere gestita da sola senza particolari preoccupazioni rispetto alla citazione e all'escaping. Quando si usano i backtick, diventa più brutto e brutto dopo i due e sopra i livelli.

    Pochi altri esempi:

     echo `echo `ls`` # INCORRECT echo `echo \`ls\`` # CORRECT echo $(echo $(ls)) # CORRECT 
  • Risolve un problema di comportamento incoerente quando si utilizzano le backquote:

    • echo '\$x' output \$x
    • echo `echo '\$x'` restituisce $x
    • echo $(echo '\$x') restituisce \$x
  • La syntax dei backtick ha limitazioni storiche sul contenuto del comando incorporato e non può gestire alcuni script validi che includono i backquote, mentre il modulo $() più recente può elaborare qualsiasi tipo di script incorporato valido.

    Ad esempio, questi script incorporati altrimenti validi non funzionano nella colonna di sinistra, ma funzionano sulla destra IEEE :

     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 ')' ` ) 

Quindi la syntax per la sostituzione del comando $ -prefixed dovrebbe essere il metodo preferito, poiché è visivamente chiara con una syntax pulita (migliora la leggibilità umana e della macchina), è nidificabile e intuitiva, la sua analisi interna è separata ed è anche più coerente ( con tutte le altre espansioni che vengono analizzate da virgolette doppie) dove i backtick sono l'unica eccezione e il carattere è facilmente mimetizzato quando è adiacente a " rendendolo ancora più difficile da leggere, specialmente con caratteri piccoli o insoliti.

Fonte: perché $(...) preferisce a `...` (backtick)? a BashFAQ

Guarda anche:

  • Sezione standard POSIX "2.6.3 Sostituzione comandi"
  • Razionale di POSIX per includere la syntax $ ()
  • Sostituzione del comando
  • bash-hacker: sostituzione del comando

Oltre alle altre risposte,

 $(...) 

spicca visivamente meglio di

 `...` 

I backtick assomigliano troppo agli apostrofi; questo varia a seconda del font che stai usando.

(E, come ho appena notato, i backtick sono molto più difficili da inserire nei campioni di codice inline.)

$() consente il nesting.

 out=$(echo today is $(date)) 

Penso che i backtick non lo permettano.

È lo standard POSIX che definisce la forma $(command) di sostituzione di comando. La maggior parte delle shell in uso oggi sono conformi a POSIX e supportano questa forma preferita rispetto alla notazione backtick arcaica. La sezione di sostituzione di comando (2.6.3) del documento Shell Language descrive questo:

La sostituzione di comando consente di sostituire l’output di un comando al posto del nome del comando stesso. La sostituzione del comando deve avvenire quando il comando è racchiuso come segue:

$( command )

o (versione backquoted):

` command `

La shell espande la sostituzione di comando eseguendo il comando in un ambiente di subshell (vedi Shell Execution Environment ) e sostituendo la sostituzione di comando (il testo del comando più l’allegato “$ ()” o backquotes) con lo standard output del comando, rimuovendo sequenze di uno o più caratteri alla fine della sostituzione. I caratteri incorporati prima della fine dell’output non devono essere rimossi; tuttavia, possono essere considerati come delimitatori di campo ed eliminati durante la divisione del campo, a seconda del valore di IFS e del quoting in vigore. Se l’output contiene byte null, il comportamento non è specificato.

All’interno dello stile backquoted della sostituzione di comando, manterrà il suo significato letterale, tranne quando seguito da: ‘ $ ‘, ‘ ` ‘ o . La ricerca del backquote corrispondente deve essere soddisfatta dal primo backquote non sfuggito; durante questa ricerca, se si incontra un backquote non di escape all’interno di un commento di shell, un here-document, una sostituzione di comando incorporata del modulo $ ( comando ) o una stringa quotata, si verificano risultati non definiti. Una stringa con virgolette singole o virgolette che inizia, ma non termina, all’interno della sequenza ” `...` ” produce risultati non definiti.

Con il modulo $ ( comando ), tutti i caratteri che seguono la parentesi aperta alla parentesi chiusa corrispondente costituiscono il comando . Qualsiasi comando di shell valido può essere utilizzato per il comando , ad eccezione di uno script costituito esclusivamente da reindirizzamenti che produce risultati non specificati.

I risultati della sostituzione di comando non devono essere elaborati per ulteriore espansione di tilde, espansione di parametri, sostituzione di comando o espansione aritmetica. Se una sostituzione di comando avviene all’interno di virgolette doppie, la divisione del campo e l’espansione del percorso non devono essere eseguite sui risultati della sostituzione.

La sostituzione di comando può essere annidata. Per specificare il nidificazione all’interno della versione riraccolta, l’applicazione deve precedere i backquotes interni con i caratteri ; per esempio:

\` command \`

La syntax del linguaggio di comando della shell ha un’ambiguità per le espansioni che iniziano con ” $(( “, che può introdurre un’espansione aritmetica o una sostituzione di comando che inizia con una sottoshell. L’espansione aritmetica ha la precedenza, cioè la shell deve prima determinare se può analizzare l’espansione come un’espansione aritmetica e deve solo analizzare l’espansione come una sostituzione di comando se determina che non può analizzare l’espansione come espansione aritmetica.La shell non ha bisogno di valutare espansioni annidate quando si esegue questa determinazione. di input senza aver già stabilito che non può analizzare l’espansione come espansione aritmetica, la shell deve considerare l’espansione come un’espansione aritmetica incompleta e riportare un errore di syntax.Una applicazione conforms deve garantire che separi ” $( ” e ” ( in due token (ovvero, separarli con uno spazio bianco) in una sostituzione di comando che inizia con una subshell. Ad esempio, un comm e la sostituzione contenente una singola sottoshell potrebbe essere scritta come:

$( ( command ) )

Questa è una domanda legacy, ma ho trovato un esempio perfettamente valido di $(...) su `...` .

Stavo usando un desktop remoto a Windows che eseguiva cygwin e volevo scorrere il risultato di un comando. Purtroppo, il carattere backtick era imansible da inserire, a causa della cosa del desktop remoto o di cygwin stesso.

È ragionevole pensare che un segno di dollaro e le parentesi siano più facili da digitare in configurazioni così strane.