Apa perbedaan antara kata kunci builtin dan kata kunci shell?

33

Ketika saya menjalankan dua perintah ini, saya mengerti

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

Jelas ditunjukkan bahwa cdshell adalah builtin dan ifkata kunci shell. Jadi apa perbedaan antara shell builtin dan kata kunci?

Avinash Raj
sumber
2
terkait: unix.stackexchange.com/questions/267761/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Jawaban:

45

Ada perbedaan kuat antara builtin dan kata kunci, dalam cara Bash mem-parsing kode Anda. Sebelum kita berbicara tentang perbedaannya, mari kita daftarkan semua kata kunci dan bawaan:

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                          

Kata kunci:

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

Perhatikan bahwa, misalnya [adalah builtin dan itu [[adalah kata kunci. Saya akan menggunakan keduanya untuk menggambarkan perbedaan di bawah ini, karena mereka adalah operator terkenal: semua orang tahu mereka dan menggunakannya secara teratur (atau harus).

Kata kunci dipindai dan dipahami oleh Bash sangat awal dalam penguraiannya. Ini memungkinkan misalnya sebagai berikut:

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

Ini berfungsi dengan baik, dan Bash akan dengan senang hati mengeluarkan

The string is non-empty

Perhatikan bahwa saya tidak mengutip $string_with_spaces. Sedangkan yang berikut ini:

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

menunjukkan bahwa Bash tidak bahagia:

bash: [: too many arguments

Mengapa ini bekerja dengan kata kunci dan tidak dengan builtin? karena ketika Bash mem-parsing kode itu, ia melihat [[mana yang merupakan kata kunci, dan memahami sangat awal bahwa itu istimewa. Jadi akan mencari penutup ]]dan akan memperlakukan bagian dalam dengan cara yang khusus. Builtin (atau perintah) diperlakukan sebagai perintah aktual yang akan dipanggil dengan argumen. Dalam contoh terakhir ini, bash memahami bahwa ia harus menjalankan perintah [dengan argumen (ditampilkan satu per baris):

-n
some
spaces
here
]

sejak ekspansi variabel, penghapusan kutipan, ekspansi pathname dan pemisahan kata terjadi. Perintah itu [ternyata dibangun di shell, jadi dieksekusi dengan argumen ini, yang menghasilkan kesalahan, karenanya keluhan.

Dalam praktiknya, Anda melihat bahwa perbedaan ini memungkinkan perilaku canggih, yang tidak mungkin dilakukan dengan builtin (atau perintah).

Masih dalam praktiknya, bagaimana Anda bisa membedakan builtin dari kata kunci? ini adalah eksperimen yang menyenangkan untuk dilakukan:

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

Ketika Bash mem-parsing baris $a -d . ], ia melihat tidak ada yang istimewa (yaitu, tidak ada alias, tidak ada pengalihan, tidak ada kata kunci), jadi itu hanya melakukan ekspansi variabel. Setelah ekspansi variabel, terlihat:

[ -d . ]

jadi jalankan perintah (builtin) [dengan argumen -d, .dan ], yang, tentu saja benar (ini hanya menguji apakah .sebuah direktori).

Sekarang lihat:

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

Oh Itu karena ketika Bash melihat baris ini, ia melihat tidak ada yang istimewa, dan karenanya memperluas semua variabel, dan akhirnya melihat:

[[ -d . ]]

Saat ini, pemanjangan alias dan pemindaian kata kunci telah lama dilakukan dan tidak akan dilakukan lagi, jadi Bash mencoba menemukan perintah yang dipanggil [[, tidak menemukannya, dan mengeluh.

Sepanjang garis yang sama:

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

dan

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

Ekspansi alias juga sesuatu yang agak istimewa. Anda semua telah melakukan yang berikut setidaknya sekali:

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

Alasannya sama: ekspansi alias terjadi jauh sebelum ekspansi variabel dan penghapusan kutipan.


Kata kunci vs Alias

Sekarang menurut Anda apa yang terjadi jika kami menetapkan alias untuk menjadi kata kunci?

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

Oh, berhasil! jadi alias bisa digunakan untuk kata kunci alias! bagus untuk mengetahui.


Kesimpulan: builtin benar-benar berperilaku seperti perintah: mereka berhubungan dengan tindakan yang dieksekusi dengan argumen yang mengalami ekspansi variabel langsung dan pemisahan kata dan globbing. Ini benar-benar seperti memiliki perintah eksternal di suatu tempat di /binatau /usr/binyang disebut dengan argumen yang diberikan setelah ekspansi variabel, dll. Perhatikan bahwa ketika saya mengatakan itu benar-benar seperti memiliki perintah eksternal yang saya maksud dengan hanya berkenaan dengan argumen, pemisahan kata, penggumpalan, ekspansi variabel, dll. builtin dapat memodifikasi keadaan internal shell!

Kata kunci, di sisi lain, dipindai dan dipahami sangat awal, dan memungkinkan untuk perilaku shell canggih: shell akan dapat melarang pemisahan kata atau perluasan pathname, dll.

Sekarang lihat daftar bawaan dan kata kunci dan coba cari tahu mengapa beberapa perlu kata kunci.


!adalah kata kunci. Tampaknya mungkin untuk meniru perilakunya dengan suatu fungsi:

not() {
    if "$@"; then
        return false
    else
        return true
    fi
}

tapi ini akan melarang konstruksi seperti:

$ ! ! true
$ echo $?
0

atau

$ ! { true; }
echo $?
1

Sama untuk time: lebih kuat untuk memiliki kata kunci sehingga dapat mengatur waktu perintah kompleks dan saluran pipa dengan pengalihan:

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

Jika di timemana hanya perintah (bahkan builtin), itu hanya akan melihat argumen grep, ^#dan /home/gniourf/.bashrc, waktu ini, dan kemudian hasilnya akan melewati bagian-bagian yang tersisa dari pipa. Tetapi dengan kata kunci, Bash dapat menangani semuanya! itu bisa timemenyelesaikan pipa, termasuk pengalihan! Jika timehanya perintah, kami tidak bisa melakukan:

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

Cobalah:

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

Coba perbaiki (?):

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

Putus asa.


Kata kunci vs alias?

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

Menurut Anda apa yang terjadi?


Sungguh, builtin seperti sebuah perintah, kecuali itu dibangun di shell, sedangkan kata kunci adalah sesuatu yang memungkinkan untuk perilaku canggih! kita dapat mengatakan itu bagian dari tata bahasa shell.

gniourf_gniourf
sumber
2
Setuju dengan @JohnyTex, ini adalah salah satu jawaban paling komprehensif dan tepat yang pernah saya lihat di situs Stack. Terima kasih. Satu pertanyaan mungkin tidak berhubungan: hanya demi rasa ingin tahu, aku sedang berusaha mencari dokumentasi untuk 'sementara menonaktifkan alias' fungsi dari sebelumnya perintah dengan =\'dengan menggunakan man, apropos, dan helpdan saya sudah tidak punya keberuntungan. Adakah tahu ke mana saya akan pergi mencari info ini? Sebagian besar sehingga di masa depan saya bisa melihat apa lagi yang ada di sana, karena saya pikir saya kehilangan sumber referensi.
nc.
@nc: Anda tidak akan menemukannya didokumentasikan secara eksplisit. Alasan kerjanya dijelaskan dalam jawaban ini. Yang paling dekat Anda akan temukan dalam manual referensi di bagian Operasi Shell . Anda akan melihat bahwa ekspansi alias dilakukan sangat awal (sesuatu yang saya coba tekankan dalam jawaban ini), pada langkah 2. Penghapusan kutipan, ekspansi parameter, globbing, dll dilakukan nanti. Jadi untuk menonaktifkan alias, Anda dapat menggunakan semacam kutipan untuk melarang shell memahami token sebagai alias, misalnya \ll,, "ll"atau 'll'.
gniourf_gniourf
1
Saya benar-benar mendapatkan pesanan, alias dan kata kunci dipecat terjadi terlebih dahulu. Karena kami mengutip [[hasil, \[[itu tidak diurai sebagai alias. Benar sejauh ini? Di mana saya tersesat tidak menyadari backslash adalah hal mengutip di Bash, dan saya mencoba mencarinya di bawah alias dan kata kunci dan benar-benar hilang ... Semuanya baik-baik saja sekarang. Di bawah bagian Quoting:> Backslash () yang tidak dikutip adalah karakter pelarian.
nc.
1
Jadi kita dapat menganggap (2) dalam daftar Op itu sebagai Tokenisasi, dan \[[merupakan token tunggal dari tipe LiteralQuoteToken, yang berlawanan dengan [[yang akan menjadi OpenTestKeywordToken, dan yang memerlukan penutupan ]]CloseTestKeywordToken untuk kompilasi oroper / sintaks yang benar. Kemudian, dalam (4) LiteralQuoteToken akan dievaluasi [[sebagai nama bulitin / perintah untuk dieksekusi, dan dalam (6) Bash akan muntah karena tidak ada [[builtin atau perintah. Bagus. Saya mungkin melupakan detail yang tepat dari waktu ke waktu, tetapi pada saat ini cara Bash menjalankan banyak hal lebih jelas bagi saya; Terima kasih.
nc.
9

man bashmemanggil mereka SHELL BUILTIN COMMANDS. Jadi, "shell builtin" sama seperti perintah normal, seperti grep, dll., Tetapi alih-alih terkandung dalam file yang terpisah, itu dibangun ke dalam bash itu sendiri . Ini membuat mereka melakukan lebih efisien daripada perintah eksternal.

Sebuah kata kunci juga "dikodekan ke dalam Bash, tetapi tidak seperti builtin, kata kunci itu sendiri bukan perintah, tetapi subunit dari perintah konstruksi." Saya menafsirkan ini berarti bahwa kata kunci tidak memiliki fungsi sendiri, tetapi memerlukan perintah untuk melakukan apa saja. (Dari link, contoh lainnya adalah for, while, do, dan !, dan ada lebih dalam jawaban saya untuk pertanyaan Anda yang lain.)

Sparhawk
sumber
1
Fakta menarik: [[ is a shell keywordtapi [ is a shell builtin. Saya tidak tahu kenapa.
Sparhawk
1
Mungkin karena alasan historis dan standar POSIX agar sesuai dengan shell Bourne lama semaksimal mungkin, karena [ada sebagai perintah terpisah pada masa itu. [[tidak ditentukan oleh standar, sehingga pengembang dapat memilih untuk memasukkannya sebagai kata kunci atau sebagai bawaan.
Sergiy Kolodyazhnyy
1

Manual baris perintah yang datang dengan Ubuntu tidak memberikan definisi kata kunci, namun manual online (lihat sidenote) dan spesifikasi standar Bahasa Perintah POSIX Shell , lihat ini sebagai "Kata-kata yang Dicadangkan", dan keduanya memberikan daftar itu. Dari standar POSIX:

Pengakuan ini hanya akan terjadi ketika tidak ada karakter yang dikutip dan ketika kata tersebut digunakan sebagai:

  • Kata pertama dari suatu perintah

  • Kata pertama yang mengikuti salah satu kata yang dipesan selain kasus, untuk, atau dalam

  • Kata ketiga dalam perintah kasus (hanya dalam yang valid dalam kasus ini)

  • Kata ketiga dalam perintah for (hanya dalam dan lakukan adalah valid dalam kasus ini)

Kuncinya di sini adalah bahwa kata kunci / kata-kata yang dipesan memiliki makna khusus karena mereka memfasilitasi sintaksis shell, berfungsi untuk memberi sinyal blok kode tertentu seperti loop, perintah majemuk, pernyataan percabangan (jika / kasus), dll. Mereka memungkinkan pembentukan pernyataan perintah, tetapi sendiri - tidak melakukan apa-apa, dan bahkan jika Anda memasukkan kata kunci seperti for, until, case- shell akan mengharapkan pernyataan lengkap, jika tidak - kesalahan sintaks:

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

Pada tingkat kode sumber, kata-kata dicadangkan untuk bash didefinisikan dalam parese.y , sementara built-in memiliki seluruh direktori yang didedikasikan untuk mereka.

Sidenote

Indeks GNU ditampilkan [sebagai kata yang dilindungi undang-undang, namun itu sebenarnya adalah perintah bawaan. [[sebaliknya adalah kata khusus.

Lihat juga: Perbedaan antara kata kunci, kata yang dipesan, dan bawaan?

Sergiy Kolodyazhnyy
sumber