Qual è la differenza tra shell builtin e shell keyword?

28

Quando eseguo questi due comandi, ottengo

$ type cd
cd is a shell builtin
$ type if
if is a shell keyword

È chiaramente dimostrato che cd è una shell incorporata e if è una parola chiave shell. Quindi qual è la differenza tra shell builtin e keyword?

    
posta Avinash Raj 10.04.2014 - 04:47

3 risposte

38

C'è una forte differenza tra un built-in e una parola chiave, nel modo in cui Bash analizza il tuo codice. Prima di parlare della differenza, elenciamo tutte le parole chiave e i builtin:

builtin:

$ compgen -b
.         :         [         alias     bg        bind      break     
builtin   caller    cd        command   compgen   complete  compopt   
continue  declare   dirs      disown    echo      enable    eval      
exec      exit      export    false     fc        fg        getopts   
hash      help      history   jobs      kill      let       local     
logout    mapfile   popd      printf    pushd     pwd       read      
readarray readonly  return    set       shift     shopt     source    
suspend   test      times     trap      true      type      typeset   
ulimit    umask     unalias   unset     wait                          

Parole chiave:

$ compgen -k
if        then      else      elif      fi        case      
esac      for       select    while     until     do        
done      in        function  time      {         }         
!         [[        ]]        coproc              

Si noti che, ad esempio [ è un builtin e che [[ è una parola chiave. Userò questi due per illustrare la differenza di seguito, poiché sono operatori noti: tutti li conoscono e li usano regolarmente (o dovrebbero).

Una parola chiave è scansionata e compresa da Bash molto presto nella sua analisi. Ciò consente ad esempio quanto segue:

string_with_spaces='some spaces here'
if [[ -n $string_with_spaces ]]; then
    echo "The string is non-empty"
fi

Funziona bene, e Bash uscirà volentieri

The string is non-empty

Nota che non ho citato $string_with_spaces . Considerando quanto segue:

string_with_spaces='some spaces here'
if [ -n $string_with_spaces ]; then
    echo "The string is non-empty"
fi

mostra che Bash non è felice:

bash: [: too many arguments

Perché funziona con parole chiave e non con builtin? perché quando Bash analizza il codice, vede [[ che è una parola chiave e comprende molto presto che è speciale. Quindi cercherà la chiusura ]] e tratterà l'interno in un modo speciale. Un builtin (o comando) viene trattato come un comando effettivo che verrà chiamato con argomenti. In questo ultimo esempio, bash capisce che dovrebbe eseguire il comando [ con argomenti (mostrato uno per riga):

-n
some
spaces
here
]

dal momento che l'espansione delle variabili, la rimozione delle virgolette, l'espansione del nome del percorso e la divisione delle parole avvengono. Il comando [ risulta essere incorporato nella shell, quindi viene eseguito con questi argomenti, il che si traduce in un errore, da cui il reclamo.

In pratica, vedi che questa distinzione consente un comportamento sofisticato, che non sarebbe possibile con i builtin (o comandi).

Ancora in pratica, come si può distinguere un built-in da una parola chiave? questo è un divertente esperimento da eseguire:

$ a='['
$ $a -d . ]
$ echo $?
0

Quando Bash analizza la riga $a -d . ] , non vede nulla di speciale (cioè, nessun alias, nessun reindirizzamento, nessuna parola chiave), quindi esegue semplicemente l'espansione variabile. Dopo le espansioni variabili, vede:

[ -d . ]

quindi esegue il comando (builtin) [ con argomenti -d , . e ] , che, ovviamente, è vero (questo verifica solo se . è una directory).

Ora guarda:

$ a='[['
$ $a -d . ]]
bash: [[: command not found

Oh. Questo perché quando Bash vede questa linea, non vede nulla di speciale, e quindi espande tutte le variabili, e alla fine vede:

[[ -d . ]]

Al momento, le espansioni alias e la scansione delle parole chiave sono state eseguite da molto tempo e non verranno più eseguite, quindi Bash tenta di trovare il comando chiamato [[ , non lo trova e si lamenta.

Seguendo le stesse linee:

$ '[' -d . ]
$ echo $?
0
$ '[[' -d . ]]
bash: [[: command not found

e

$ \[ -d . ]
$ echo $?
0
$ \[[ -d . ]]
bash: [[: command not found

Anche l'espansione degli alias è qualcosa di speciale. Hai fatto tutto quanto segue almeno una volta:

$ alias ll='ls -l'
$ ll
.... <list of files in long format> ....
$ \ll
bash: ll: command not found
$ 'll'
bash: ll: command not found

Il ragionamento è lo stesso: l'espansione dell'alias si verifica molto prima dell'espansione delle variabili e della rimozione delle virgolette.

Parola chiave v.s. Alias ​​

Ora, cosa pensi che succeda se definiamo un alias come parola chiave?

$ alias mytest='[['
$ mytest -d . ]]
$ echo $?
0

Oh, funziona! quindi gli alias possono essere utilizzati per alias parole chiave! bello sapere.

Conclusione: i veramente builtin si comportano come i comandi: corrispondono a un'azione che viene eseguita con argomenti che subiscono espansione diretta delle variabili e divisione delle parole e globbing. È proprio come avere un comando esterno da qualche parte in /bin o /usr/bin che viene chiamato con gli argomenti dati dopo l'espansione delle variabili, ecc. Si noti che quando dico è proprio come avere un comando esterno Intendo solo per quanto riguarda argomenti, suddivisione di parole, globbing, espansione variabile, ecc. Un builtin può modificare lo stato interno della shell!

Le parole chiave, d'altra parte, vengono scansionate e comprese molto presto e consentono un comportamento sofisticato della shell: la shell sarà in grado di proibire la divisione delle parole o l'espansione del percorso, ecc.

Ora guarda l'elenco di builtin e parole chiave e prova a capire perché alcuni devono essere parole chiave.

! è una parola chiave. Sembra che sarebbe possibile imitare il suo comportamento con una funzione:

not() {
    if "[email protected]"; then
        return false
    else
        return true
    fi
}

ma questo vieterebbe costrutti come:

$ ! ! true
$ echo $?
0

o

$ ! { true; }
echo $?
1

Uguale a time : è più potente avere una parola chiave in modo che possa temporizzare complessi comandi composti e pipeline con i reindirizzamenti:

$ time grep '^#' ~/.bashrc | { i=0; while read -r; do printf '%4d %s\n' "$((++i))" "$REPLY"; done; } > bashrc_numbered 2>/dev/null

Se time dove un semplice comando (anche incorporato), vedrebbe solo gli argomenti grep , ^# e /home/gniourf/.bashrc , ora questo, e quindi il suo output passerebbe attraverso le restanti parti della pipeline . Ma con una parola chiave, Bash può gestire tutto! può time la pipeline completa, inclusi i reindirizzamenti! Se time fosse un semplice comando, non potremmo fare:

$ time { printf 'hello '; echo world; }

Prova:

$ \time { printf 'hello '; echo world; }
bash: syntax error near unexpected token '}'

Prova a risolvere il problema (?):

$ \time { printf 'hello '; echo world;
time: cannot run {: No such file or directory

Hopeless.

Parola chiave vs alias?

$ alias mytime=time
$ alias myls=ls
$ mytime myls

Che cosa pensi che accada?

In realtà, un builtin è come un comando, tranne che è incorporato nella shell, mentre una parola chiave è qualcosa che consente un comportamento sofisticato! possiamo dire che fa parte della grammatica della shell.

    
risposta data gniourf_gniourf 26.02.2015 - 19:57
8

man bash li chiama SHELL BUILTIN COMMANDS . Quindi, una "shell builtin" è come un normale comando, come grep , ecc., Ma invece di essere contenuto in un file separato, è integrato nella bash stessa . Ciò li rende più efficienti rispetto ai comandi esterni.

Una parola chiave è anche "hard-coded in Bash, ma a differenza di un builtin , una parola chiave non è di per sé un comando, ma una subunità di un costrutto di comando. " Interpreto questo per indicare che le parole chiave non hanno alcuna funzione, ma richiedono comandi per fare qualsiasi cosa. (Dal link, altri esempi sono for , while , do e ! , e ce ne sono di più in la mia risposta alla tua altra domanda.)

    
risposta data Sparhawk 10.04.2014 - 04:58
1

Il manuale della riga di comando fornito con Ubuntu non fornisce una definizione di parole chiave, tuttavia manuale online (vedere sidenote) e specifiche standard del linguaggio di comando Shell POSIX , fa riferimento a questi come "Parole riservate" e forniscono entrambi elenchi. Dallo standard POSIX:

  

Questo riconoscimento si verifica solo quando nessuno dei caratteri è quotato e quando la parola è usata come:

     
  • La prima parola di un comando

  •   
  • La prima parola che segue una delle parole riservate diverse da case, for o in

  •   
  • La terza parola nel comando case (solo in è valida in questo caso)

  •   
  • La terza parola in un comando for (solo in e do sono validi in questo caso)

  •   

La chiave qui è che le parole chiave / parole riservate hanno un significato speciale perché facilitano la sintassi della shell, servono a segnalare determinati blocchi di codice come loop, comandi composti, istruzioni branching (if / case), ecc. Permettono il comando di formatura affermazioni, ma da sole - non fare nulla, e infatti se inserisci parole chiave come for , until , case - la shell si aspetta un'istruzione completa, altrimenti - errore di sintassi:

$ for
bash: syntax error near unexpected token 'newline'
$  

Al livello del codice sorgente, le parole riservate per bash sono definite in parese. y , mentre i built-in hanno l'intera directory dedicata a loro.

Sidenote

L'indice GNU mostra [ come parola riservata, tuttavia è un comando incorporato di fatto. [[ al contrario è una parola riservata.

Vedi anche: Differenze tra parola chiave, parola riservata e builtin

    
risposta data Sergiy Kolodyazhnyy 16.07.2018 - 00:41

Leggi altre domande sui tag