内建命令指的就是包含在Bash工具包中的命令, 从字面意思上看就是built in. 这主要是考虑到执行效率的问题 – 内建命令将比外部命令执行的更快, 一部分原因是因为外部命令通常都需要fork出一个单独的进程来执行 – 另一部分原因是特定的内建命令需要直接访问shell的内核部分.
当一个命令或者是shell本身需要初始化(或者创建)一个新的子进程来执行一个任务的时候,这种行为被称为fork. 这个新产生的进程被叫做子进程, 并且这个进程是从父进程中fork出来的. 当子进程执行它的任务时, 父进程也在运行.
I/O
echo
打印(到 stdout )一个表达式或者变量.
* echo命令可以作为输入, 通过管道传递到一系列命令中去.
if echo "$avr" | grep -q txt #if [[ $var = *txt* ]]
then
echo "$var contains the substring sequence \"txt\"."
fi
- echo命令可以与命令替换组合起来, 这样可以用来设置一个变量.
a=`echo "HELLO" | tr A-Z a-z`
# tr,translate的简写,主要用于压缩重复字符,删除文件中的控制字符以及进行字符转换操作.
echo $a # hello
- 小心echo `command`将会删除任何由command 所产生的换行符.
printf
printf命令,格式化输出,是echo的增强版,是C语言printf()库函数的一个有限的变形,但在语法上有些不同.
printf format-string… parameter…
例:使用printf的例子.
#!/bin/bash
# printf 示例
PI=3.14159265358979
DecimalConstant=31373
Message1="Greetings,"
Message2="Earthling."
echo
printf "pi to 2 decimal places = %1.2f" $PI
echo
printf "pi to 9 decimal places = %1.9f" $PI
printf "\n" # 打印一个换行,等价与 'echo'
printf "Constant = \t%d\n" $DecimalConstant # 插入一个 tab (\t).
printf "%s %s \n" $Message1 $Message2
echo
pi12=$(printf "%1.12f" $PI)
echo "pi to 12 decimal places = $pi12"
Msg=`printf "%s %s \n" $Message1 $Message2`
echo $Msg
exit 0
- 使用printf的最主要的应用就是格式化错误消息.
read
从 stdin 中”读取”一个变量的值, 也就是和键盘进行交互, 来取得变量的值. 使用 -a 参数可以read数组变量.
例:使用read来进行变量分配.
#!/bin/bash
# "Reading" 变量.
echo -n "Enter the value of variable 'var1': " # -n 选项,阻止换行.
read var1 #注意:在var1前面没有'$',因为变量正在被设置.
echo "var1 = $var1"
echo -n "Enter the values of variables 'var2' and 'var3' (separated by a space or tab): "
read var2 var3
echo "var2 = $var2 var3 = $var3"
# 如果你只输入了一个值,那么其他变量还是处于未设置状态(null).
exit 0
一个不带变量参数的read命令, 将会把来自键盘的输入存入到专用变量$REPLY中.
例: 不带变量参数的read命令.
#!/bin/bash
# read-novar.sh
echo -n "Enter a value: "
read var # 带变量参数的'read'命令.
echo "\"var\" = $var"
echo -n "Enter another value: "
read # 没有变量分配给'read'命令,输入将分配给默认变量,$REPLY.
var="$REPLY"
echo "\"var\" = $var"
exit 0
一般的, 当输入给read时, 输入一个 \ , 然后回车, 将会阻止产生一个新行. -r 选项将会让 \ 转移.
例: read 命令的多行输入.
#!/bin/bash
# read 命令的多行输入
echo "Enter a string terminated by a \\, then press <ENTER>."
echo "Then, enter a second string, and again press <ENTER>."
read var1 # 当 read $var1 时, "\" 将会阻止产生新行.
#+ first line \
#+ second line.
echo "var1 = $var1"
echo
echo "Enter another string terminated by a \\ , then press <ENTER>."
read -r var2 # -r 会让"\"转义.
# first line\
echo "var2 = $var2" # first line \
# 第一个<ENTER>就会结束2变量的输入.
echo
exit 0
read命令有些有趣的选项, 这些选项允许打印出一个提示符, 然后在不输入ENTER的情况下, 可以读入你所按下的字符的内容.
# 不敲回车,读取一个按键字符.
read -s -n1 -p "Hit a Key " Keypress
echo;echo "Keypress was "\"$Keypress\"."
# -s 选项意味着不打印输入.
# -n N 选项意味着只接受N个字符的输入.
# -p 选项意味着在读取输入之前打印出后面的提示符.
read命令也可以从重定向的文件中”读取”变量的值. 如果文件中的内容超过一行, 那么只有第一行被分配到这个变量中. 如果read命令的参数个数超过一个, 那么每个变量都会从文件中取得一个分配的字符串作为变量的值, 这些字符串都是以定义的空白字符来进行分隔的. 小心使用!
例子: 通过文件重定向来使用read命令
#!/bin/bash
# read data from file.
read var1 < data_file # var1 将会把"data_file"的第一行的全部内容都作为它的值.
echo "var1 = $var1"
read var2 var3 < data_file
echo "var2 = $var2 var3 = $var3"
# 注意,这里的"read"命令将会产生一种不直观的行为.
# 1> 重新从文件的头部开始读入变量.
# 2> 每个变量都设置成了以空白分割的字符串,而不是之前的以整行的内容作为变量的值.
# 3> 而最后一个变量将会取得第一行剩余的全部内容.(不管是否以空白分割).
# 4> 如果需要赋值的变量个数比文件中第一行以空白分割的字符串个数还多的话,那么这些变量将会被赋空值.
echo "----------------------------------------------"
while read line
do
echo "$line"
done < data_file
echo "----------------------------------------------"
echo "List of all users:"
OIFS=$IFS;IFS=: # /etc/passwd 使用":"作为域分隔符.
while read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # I/O 重定向.
IFS=$OIFS # 恢复原始的$IFS.
echo "----------------------------------------------"
echo "List of all users:"
while IFS=: read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done </etc/passwd # I/O 重定向.
echo
echo "\$IFS still $IFS"
exit 0
文件系统
cd
cd, 修改目录命令, 在脚本中用的最多的时候就是当命令需要在指定目录下运行时, 需要用它来修改当前工作目录.
-p (physical)选项对于cd命令的意义是忽略符号链接.
cd - 将会把工作目录修改至$OLDPWD,也就是之前的工作目录.
pwd
打印出当前的工作目录. 这将给出用户(或脚本)的当前工作目录. 使用这个命令的结果和从内建变量$PWD中所读取的值是相同的.
pushd, popd, dirs
这几个命令可以使得工作目录书签化, 就是可以按顺序向前或向后移动工作目录. 压栈的动作可以保存工作目录列表. 选项可以允许对目录栈做不同的操作.
pushd dir-name 把路径 dir-name 压入目录栈,同时修改当前目录到 dir-name .
popd将目录栈最上边的目录弹出, 同时将当前目录修改为刚弹出来的那个目录.
dirs列出所有目录栈的内容 (与$DIRSTACK变量相比较). 一个成功的pushd或者popd将会自动调用dirs命令.
变量
let
let命令将执行变量的算术操作. 在许多情况下, 它被看作是复杂的expr命令的一个简化版本.
eval
eval arg1 [arg2] … [argN]
* eval命令是有风险的, 如果你有更合适的方法来实现功能的话, 尽量避免使用它. eval $COMMANDS 将会执行命令 COMMANDS 的内容, 如果命令中包含有rm-rf *这样的东西, 可能就不是你想要的了. 当你运行一个包含有eval命令的陌生人所编写的代码片段的时候, 这是一件很危险的事情.
set
set命令用来修改内部脚本变量的值. 它的一个作用就是触发选项标志位来帮助决定脚本的行为.另一个作用是以一个命令的结果( set `command` )来重新设置脚本的位置参数. 脚本将会从命令的输出中重新分析出位置参数.
unset
unset 命令用来删除一个shell变量,这个变量的效果就是把这个变量设为null. 注意:这个命令对位置参数无效.
例: unset 一个变量.
#!/bin/bash
# unset.sh: Unset 一个变量.
variable=hello # 初始化.
echo "variable = $variable" # variable = hello
unset variable # Unset.
echo "(unset) variable = $variable" # (unset) variable =
export
export命令将会使得被export的变量在所运行脚本(或shell)的所有子进程中都可用. 不幸的是,没有办法将变量export到父进程中, 这里所指的父进程就是调用这个脚本的脚本或shell. 关于export命令的一个重要的用法就是使用在启动文件中, 启动文件用来初始化和设置环境变量,这样, 用户进程才能够访问环境变量.
* 可以在一个操作中同时进行赋值和export变量, 比如: export var1=xxx.
declare, typedef
declare 和 typedef命令被用来指定或限制变量的属性.
readonly
与declare -r 作用相同,设置变量的只读属性,或者可以认为这个变量就是一个常量.
getopts
可以说这个命令是分析传递到脚本中命令行参数的最强力的工具. 这个命令与外部命令getopt,还有C 语言中的库函数getopt的作用是相同的. 它允许传递和连接多个选项到脚本中, 并且能够分配多个参数到脚本中.
getopts结构使用两个隐含变量. $OPTIND 是参数指针( 选项索引) 和 $OPTARG ( 选项参数)(可选的)可以在选项后边附加一个参数. 在声明标签中, 选项名后边的冒号用来提示这个选项名已经分配了一个参数.
脚本行为
souece, . (点 命令)
当在命令行中调用的时候, 这个命令将会执行一个脚本. 当在脚本中调用的时候, source file-name 将会加载 file-name 文件. sourc一个文件(或点命令)将会在脚本中 引入代码, 并将这些代码附加到脚本中(与 C 语言中的 #include 指令效果相同). 最终的结果就像是在使用”source”的行上插入了相应文件的内容. 在多个脚本需要引用相同的数据, 或者需要使用函数库的情况下, 这个命令非常有用.
例: “includ”一个数据文件.
#!/bin/bash
.data-file #加载一个数据文件.
# 与"source data-file"效果相同,但是更具有可
移植性.
# 文件"data-file"必须存在于当前工作目录,因为这个文件是使用'basename'来引用的.
# 现在引用文件中的一些数据.
echo "variable1 (from data-file) = $variable1"
echo "variable3 (from data-file) = $variable3"
let "sum = $variable2 + $variable4"
echo "Sum of variable2 + variable4 (from data-file) = $sum"
echo "message1 (from data-file) is \"$message1\""
print_message This is the message-print function in the data-file.
exit 0
* 上面例子所使用的文件data-file, 必须和上边的脚本放在同一目录下.
# 这是需要被脚本加载的数据文件.
# 这个文件可以包含变量, 函数, 等等.
# 在脚本中可以通过'source'或者'.'命令来加载.
# 让我们初始化一些变量.
variable1=22
variable2=474
variable3=5
variable4=97
message1="Hello, how are you?"
message2="Enough fo now. Goodbye."
print_message()
{
#echo 出所有传递进来的消息.
if [ -z "$1" ]
then
return 1
fi
echo
until [ -z "$1"]
do
echo -n "$1"
ecoh -n " "
shift
done
echo
return 0
}
如果source进来的文件的本身就是一个可执行脚本的话,那么它将运行起来,然后将控制权交还给调用它的脚本. 一个source进来的可执行脚本可以使用return命令来达到这个目的.
也可向source文件中传递参数,这些参数将被看作位置参数.
source $filename $arg1 arg2
例: 一个source自身的脚本.
#!/bin/bash
# self-source.sh : 一个脚本"递归"的source自身.
Maxpasscnt=100
let "pass_count += 1"
echo -n "$pass_count "
while [ "$pass_count" -lt $Maxpasscnt ]
do
. $0
# ./$0 # ./$0(应该能够正常递归)不能在这正常运行.
done
echo
exit 0
exit
无条件的停止一个脚本的运行. exit命令可以随意的取得一个整数参数, 然后把这个参数作为这个脚本的退出状态码. 在退出一个简单脚本的时候, 使用 exit 0 的话, 是种好习惯, 因为这表明成功运行.
如果不带参数调用exit命令退出的话, 那么退出状态码将会将会是脚本中最后一个命令的退出状态码. 等价于exit $?.
exec
这个shell内建命令将使用一个特定的命令来取代当前进程. 一般的当shell遇到一个命令, 它会forks off一个子进程来真正的运行命令. 使用exec内建命令, shell就不会fork了, 并且命令的执行将会替换掉当前shell. 因此, 在脚本中使用时, 一旦exec所执行的命令执行完毕, 那么它就会强制退出脚本.
例: exec命令的效果.
#!/bin/bash
# exec命令的效果.
exec echo "Exiting \"$0\"." # 脚本应该在这里退出.
# The following lines never exectue.
echo "This echo will never echo."
exit 99 # 脚本是不会在这里退出的.
例:一个exec自身的脚本.
#!/bin/bash
# self-exec.sh
echo
echo "This line appears ONCE in the script, yet it keeps echoing."
echo "The PID of this instance of the script is still $$."
# 上面这行展示了并没有fork出子shell.
echo "=============== Hit Ctl-C to exit ================"
sleep 1
exec $0
echo "This line will never echo!"
exit 0
运行:
bash$ ./self-exec.sh
This line appears ONCE in the script, yet it keeps echoing.
The PID of this instance of the script is still 9951.
=============== Hit Ctl-C to exit ================
This line appears ONCE in the script, yet it keeps echoing.
The PID of this instance of the script is still 9951.
=============== Hit Ctl-C to exit ================
This line appears ONCE in the script, yet it keeps echoing.
The PID of this instance of the script is still 9951.
=============== Hit Ctl-C to exit ================
This line appears ONCE in the script, yet it keeps echoing.
The PID of this instance of the script is still 9951.
=============== Hit Ctl-C to exit ================
This line appears ONCE in the script, yet it keeps echoing.
The PID of this instance of the script is still 9951.
=============== Hit Ctl-C to exit ================
^C
bash$
# exec命令还能够用来重新分配文件描述符. 比如:
exec<zzz-file # 将会用 zzz-file 来代替 stdin.
* find命令的 -exec 选项与shell内建的exec命令是不同的.
caller
将caller命令放到函数中,将会在stdout上打印出函数的调用者信息.
#!/bin/bash
# caller.sh
function1 () {
# 在 function1 () 内部.
caller 0 # 显示调用者信息.
}
function1
caller 0
exit 0 # 没效果,因为这个命令不再函数中.
运行:
bash$ ./caller.sh
9 main ./caller.sh
命令
true
这是一个返回(零)成功退出状态码的命令, 但是除此之外不做任何事.
# 死循环
while true # 这里的true可以用":"来替换.
do
operation-1
operation-2
...
operation-n
done
false
这是一个返回失败退出状态码的命令, 但是除此之外不做任何事.
type [cmd]
与外部命令which很相像, type cmd将会给出”cmd”的完整路径. 与which命令不同的是, type命令是Bash内建命令. -a 是type命令的一个非常有用的选项, 它用来鉴别参数是 关键字 还是 内建命令 ,也可以用来定位同名的系统命令.
bash$ type '['
[ is a shell builtin
bash$ type -a ']'
[ is a shell builtin
[ is /usr/bin/[
hash [cmds]
在shell的hash表中, 记录指定命令的路径名, 所以在shell或脚本中调用这个命令的话, 就不需要再在 $PATH 中重新搜索这个命令了. 如果不带参数的调用hash命令, 它将列出所有已经被hash的命令. -r 选项会重新设置hash表.
bind
bind内建命令用来显示或修改readline的键绑定.
help
获得shell内建命令的一个小的使用总结. 与whatis命令比较象, 但help命令是内建命令.
bash$ help exit
exit: exit [n]
Exit the shell with a status of N. If N is omitted, the exit status
is that of the last command executed.