Існує сильна різниця між вбудованим і ключовим словом у тому, як Bash аналізує ваш код. Перш ніж ми поговоримо про різницю, перерахуємо всі ключові слова та вбудовані:
Вбудовані:
$ 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
Ключові слова:
$ compgen -k
if then else elif fi case
esac for select while until do
done in function time { }
! [[ ]] coproc
Зауважте, що, наприклад [
, це вбудований, і [[
це ключове слово. Я буду використовувати ці два, щоб проілюструвати різницю нижче, оскільки вони є відомими операторами: їх усі знають і регулярно (або повинні) використовувати.
Ключове слово сканується та розуміється Bash дуже рано при його розборі. Це дозволяє, наприклад, таке:
string_with_spaces='some spaces here'
if [[ -n $string_with_spaces ]]; then
echo "The string is non-empty"
fi
Це добре працює, і Баш із задоволенням виведе
The string is non-empty
Зауважте, що я не цитував $string_with_spaces
. Беручи до уваги наступне:
string_with_spaces='some spaces here'
if [ -n $string_with_spaces ]; then
echo "The string is non-empty"
fi
показує, що Баш не задоволений:
bash: [: too many arguments
Чому він працює з ключовими словами, а не з вбудованими? тому що, коли Bash розбирає код, він бачить, [[
яке ключове слово, і дуже рано розуміє, що це особливе. Так він буде шукати закриття ]]
і буде обробляти всередині особливим чином. Вбудована (або командна) трактується як фактична команда, яку потрібно викликати аргументами. У цьому останньому прикладі bash розуміє, що він повинен запускати команду [
з аргументами (показаними по одному на рядок):
-n
some
spaces
here
]
оскільки відбувається змінне розширення, видалення цитат, розширення назви шляху та розбиття слів. Команда [
виявляється вбудованою в оболонку, тому вона виконує її цими аргументами, що призводить до помилки, отже, і скарга.
На практиці ви бачите, що ця відмінність забезпечує складну поведінку, що було б неможливо з вбудованими (або командами).
На практиці, як ви можете відрізнити вбудований ключ від ключового слова? це цікавий експеримент:
$ a='['
$ $a -d . ]
$ echo $?
0
Коли Bash розбирає рядок $a -d . ]
, він не бачить нічого особливого (тобто, ні псевдоніми, ні перенаправлення, ні ключові слова), тому він просто виконує змінну розширення. Після змінних розширень він бачить:
[ -d . ]
так виконується команда (вбудована) [
з аргументами -d
, .
і ]
, звичайно, це правда (це лише тестує, чи .
є каталог).
А тепер подивіться:
$ a='[['
$ $a -d . ]]
bash: [[: command not found
Ой. Це тому, що коли Баш бачить цю лінію, він не бачить нічого особливого, а значить, розширює всі змінні, і врешті-решт бачить:
[[ -d . ]]
Наразі розширення псевдонімів і сканування ключових слів уже давно виконуються і більше не виконуватимуться, тому Баш намагається знайти команду, яку називають [[
, не знаходить її, і скаржиться.
За тими ж лініями:
$ '[' -d . ]
$ echo $?
0
$ '[[' -d . ]]
bash: [[: command not found
і
$ \[ -d . ]
$ echo $?
0
$ \[[ -d . ]]
bash: [[: command not found
Розширення псевдонімів - це теж щось особливе. Ви хоч раз зробили таке:
$ alias ll='ls -l'
$ ll
.... <list of files in long format> ....
$ \ll
bash: ll: command not found
$ 'll'
bash: ll: command not found
Міркування ті ж: псевдонім розширення відбувається задовго до змінного розширення та видалення цитат.
Ключове слово проти псевдоніма
Тепер, що ви думаєте, що станеться, якщо ми визначимо псевдонім ключовим словом?
$ alias mytest='[['
$ mytest -d . ]]
$ echo $?
0
О, це працює! тому псевдоніми можна використовувати для псевдонімів ключових слів! добре знати.
Висновок: вбудовані дійсно поводяться як команди: вони відповідають дії, що виконується аргументами, які зазнають прямого розширення змінної та розбиття слів та глобалізації. Це дійсно так, як мати зовнішню команду десь /bin
або, /usr/bin
яку називають аргументами, поданими після розширення змінної, і т. Д. Зауважте, що, коли я кажу, це дійсно так, як мати зовнішню команду, я маю на увазі лише аргументи, розділення слів, глобалізацію, змінне розширення тощо. Вбудований може змінювати внутрішній стан оболонки!
Ключові слова, з іншого боку, скануються та розуміються дуже рано та дозволяють отримати складну поведінку оболонки: оболонка зможе заборонити розбиття слів або розширення назви шляху тощо.
Тепер подивіться список вбудованих і ключових слів і спробуйте розібратися, чому деякі повинні бути ключовими словами.
!
- ключове слово. Здається, можна було б імітувати його поведінку функцією:
not() {
if "$@"; then
return false
else
return true
fi
}
але це забороняло б такі конструкції:
$ ! ! true
$ echo $?
0
або
$ ! { true; }
echo $?
1
Те саме для time
: це більш потужне, щоб у ньому було ключове слово, щоб він міг обробляти складні команди та конвеєри з перенаправленнями:
$ time grep '^#' ~/.bashrc | { i=0; while read -r; do printf '%4d %s\n' "$((++i))" "$REPLY"; done; } > bashrc_numbered 2>/dev/null
Якщо time
де проста команда (навіть вбудована), вона бачила б лише аргументи grep
, ^#
і /home/gniourf/.bashrc
, час це, і тоді її вихід проходив би через інші частини конвеєра. Але за допомогою ключового слова Bash впорається з усім! він може time
завершити трубопровід, включаючи перенаправлення! Якби це time
була просто команда, ми не могли б виконати:
$ time { printf 'hello '; echo world; }
Спробуй це:
$ \time { printf 'hello '; echo world; }
bash: syntax error near unexpected token `}'
Спробуйте виправити (?) Це:
$ \time { printf 'hello '; echo world;
time: cannot run {: No such file or directory
Безнадійні.
Ключове слово порівняно з псевдонімом?
$ alias mytime=time
$ alias myls=ls
$ mytime myls
Як ви думаєте, що трапляється?
Дійсно, вбудований схожий на команду, за винятком того, що він вбудований в оболонку, тоді як ключове слово - це те, що дозволяє вишукану поведінку! можна сказати, що це частина граматики оболонки.