今天查看linux脚本的时候发现一个命令read,原本脚本中使用read并没有什么问题,突然好奇read是怎么把最后一个参数设定为变量的。
which read
/usr/bin/read
ls -al /usr/bin/read
-rwxr-xr-x. 1 root root 28 Mar 31 2020 /usr/bin/read
平常使用的命令都是程序+参数,参数是传递给程序的main函数,通过识别main函数参数决定程序接下来要执行的流程,程序本身是二进制可执行文件。看到read命令本身大小只有28个字节,肯定不是二进制程序,于是看了一下read文件的内容
cat /usr/bin/read
#!/bin/sh
builtin read "$@"
看到这里发现read是个builtin命令,$@就是将参数全部传递到builtin read,包括最后一个代表变量的参数。既然是脚本,就可以按照脚本的方式新建变量。查阅了一部分builtin相关资料后发现linux终端提供了help命令,里面就是builtin命令
help
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
These shell commands are defined internally. Type `help' to see this list.
Type `help name' to find out more about the function `name'.
Use `info bash' to find out more about the shell in general.
Use `man -k' or `info' to find out more about commands not in this list.
A star (*) next to a name means that the command is disabled.
job_spec [&] history [-c] [-d offset] [n] or hist>
(( expression )) if COMMANDS; then COMMANDS; [ elif C>
. filename [arguments] jobs [-lnprs] [jobspec ...] or jobs >
: kill [-s sigspec | -n signum | -sigs>
[ arg... ] let arg [arg ...]
[[ expression ]] local [option] name[=value] ...
alias [-p] [name[=value] ... ] logout [n]
bg [job_spec ...] mapfile [-n count] [-O origin] [-s c>
bind [-lpvsPVS] [-m keymap] [-f filen> popd [-n] [+N | -N]
break [n] printf [-v var] format [arguments]
builtin [shell-builtin [arg ...]] pushd [-n] [+N | -N | dir]
caller [expr] pwd [-LP]
case WORD in [PATTERN [| PATTERN]...)> read [-ers] [-a array] [-d delim] [->
cd [-L|[-P [-e]]] [dir] readarray [-n count] [-O origin] [-s>
command [-pVv] command [arg ...] readonly [-aAf] [name[=value] ...] o>
compgen [-abcdefgjksuv] [-o option] > return [n]
complete [-abcdefgjksuv] [-pr] [-DE] > select NAME [in WORDS ... ;] do COMM>
compopt [-o|+o option] [-DE] [name ..> set [-abefhkmnptuvxBCHP] [-o option->
continue [n] shift [n]
coproc [NAME] command [redirections] shopt [-pqsu] [-o] [optname ...]
declare [-aAfFgilrtux] [-p] [name[=va> source filename [arguments]
dirs [-clpv] [+N] [-N] suspend [-f]
disown [-h] [-ar] [jobspec ...] test [expr]
echo [-neE] [arg ...] time [-p] pipeline
enable [-a] [-dnps] [-f filename] [na> times
eval [arg ...] trap [-lp] [[arg] signal_spec ...]
exec [-cl] [-a name] [command [argume> true
exit [n] type [-afptP] name [name ...]
export [-fn] [name[=value] ...] or ex> typeset [-aAfFgilrtux] [-p] name[=va>
false ulimit [-SHacdefilmnpqrstuvx] [limit>
fc [-e ename] [-lnr] [first] [last] o> umask [-p] [-S] [mode]
fg [job_spec] unalias [-a] name [name ...]
for NAME [in WORDS ... ] ; do COMMAND> unset [-f] [-v] [name ...]
for (( exp1; exp2; exp3 )); do COMMAN> until COMMANDS; do COMMANDS; done
function name { COMMANDS ; } or name > variables - Names and meanings of so>
getopts optstring name [arg] wait [id]
hash [-lr] [-p pathname] [-dt] [name > while COMMANDS; do COMMANDS; done
help [-dms] [pattern ...] { COMMANDS ; }
read就是其中一个builtin命令,help也是,可以通过type查看命令是系统命令还是shell内置命令,type后面的参数如果是builtin命令,则type xxxx会传输xxxxis sell builtin。如果 type后面的参数不是built命令,则type xxxx输出xxxx is xxxx,如果系统中既有builtin命令也有同名的系统命令。则需要通过指定全路径的方式执行type命令。
type read
read is a shell builtin
which read
/usr/bin/read
type /usr/bin/read
/usr/bin/read is /usr/bin/read
file /usr/bin/read
/usr/bin/read: POSIX shell script, ASCII text executable
type echo
echo is a shell builtin
which echo
/usr/bin/echo
type /usr/bin/echo
/usr/bin/echo is /usr/bin/echo
file /usr/bin/echo
/usr/bin/echo: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.32,
BuildID[sha1]=c2ff2c386aeffcf4352d7c20b07920d4286d7db6, stripped
通过如上测试,直接执行type的时候,shell会从内置命令中查找,除非指定命令的绝对路径。echo命令是内置命令,/usr/bin/echo和/usr/bin/read不一样,/usr/bin/echo是二进制程序,/usr/bin/read是可执行脚本。
type命令针对内置命令和系统命令做了识别,并且输出不一样,可以通过输出区分。
Shell 内部实现了很多 builtin 的命令,其主要目的就是为了提高 shell 命令的执行效率。不同 Shell 程序实现的 builtin 命令集各不相同。为了详细了解你当前使用的 Shell 所支持的 builtin 命令,需要查看 man 手册。譬如对于我们常用的 bash,运行 man bash 后搜索 “SHELL BUILTIN COMMANDS” 关键字会有专门的章节介绍 bash 所支持的 builtin 命令。如果我们只是想快速地列出所有 bash 支持的 builtin 命令,也可以在 bash 中执行 help 命令,这个命令本身就是一个 builtin 命令。如果是想查看具体的某个 builtin 命令的使用,可以带上 -m 参数,如下:
help -m echo
NAME
echo - Write arguments to the standard output.
SYNOPSIS
echo [-neE] [arg ...]
DESCRIPTION
Write arguments to the standard output.
Display the ARGs on the standard output followed by a newline.
Options:
-n do not append a newline
-e enable interpretation of the following backslash escapes
-E explicitly suppress interpretation of backslash escapes
`echo' interprets the following backslash-escaped characters:
\a alert (bell)
\b backspace
\c suppress further output
\e escape character
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash
\0nnn the character whose ASCII code is NNN (octal). NNN can be
0 to 3 octal digits
\xHH the eight-bit character whose value is HH (hexadecimal). HH
can be one or two hex digits
Exit Status:
Returns success unless a write error occurs.
SEE ALSO
bash(1)
IMPLEMENTATION
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
由于各种 Shell 所支持的 builtin 命令各不相同,对于有些常用的命令,为避免用户当前所使用的 Shell 不支持,系统会提供同名的程序文件。譬如 echo,既是 bash 的 builtin 命令也是一个独立的命令程序。根据 bash 中执行命令的优先级,对于同名的命令,内置命令会优先被执行,所以当我们在 bash 中直接输入 echo 命令时执行的是 bash 的 buildin 命令,如果要执行独立的命令程序 echo,则需要输入全路径 /usr/bin/echo。
如果想要快速确定一个命令究竟是 bash 的 builtin 命令还是一个普通的程序命令,可以使用 type 命令进行检测。
平常脚本中执行的命令,多是builtin命令。系统中也会为了兼容脚本的执行,弥补文件的缺失,所以系统中多半会了兼容脚本的运行,创建出同名同功能的二进制程序。