bash的工作特性之命令执行状态返回值
一、shell是什么?
Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥,用户的大部分工作都是通过Shell完成的。Shell既是一种命令语言,又是一种程序设计语言。作为命令语言,它交互式地解释和执行用户输入的命令;作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。
shell是一个应用程序,是用户管理应用程序的一个接口。
二、广义上shell的分类
GUI:图形用户界面
KDE,GNOME,XFCE
CLI:命令行接口
bash, zsh, fishsh, csh, tcsh, ksh
在大多发行版中常用的为bash
三、bash的特性
bash是弱类型的编程语言,不严格区分数据类型,意味把所有数据统统当作字符串处理;
字符串类型的数据可不加引号;
引号有三种类型:', ", `
', ":字符引用
': 强引用,其内部的变量不会被替换;
":弱引用,其内部的变量会被替换;
`:命令引用
变量引用:${NAME}
a、bash特性之一:命令别名
获取当前用户可用的别名的定义:
1 # alias
定义别名:
1 # alias NAME='COMMAND'
生命周期:当前shell进程;
1 # unalias NAME
撤消别名:
b、bash的特性之二:命令历史
shell进程会保存其会话中用户曾经执行过的命令;命令通过其“历史文件”来持久保存此前执行过的命令;每个用户都有其自己专用的历史文件;
HISTSIZE:shell进程的缓冲区保留的历史命令的条数;
HISTFILESIZE:命令历史文件可保存的历史命令的条数;
默认均为1000;
1 # echo $HISTSIZE
2 1000
3 # echo $HISTFILESIZE
4 1000
HISTFILE:当前用户的命令历史文件;
~/.bash_history
查看命令历史列表:
1 # history
命令用法:
history -c:清空命令历史;
history -d OFFSET:删除指定的条目;
1 # history -d 156
-a 将当前缓冲的历史行追加到历史文件中
-n 从历史文件中读取所有未被读取的行
-r 读取历史文件并将内容追加到历史列表中
调用命令历史列表中的命令以重执行之目的:
!#:再一次执行历史列表中的第#条命令;
1 # history
2 ....
3 72 cat test
4 73 history
5 # !72
6 cat test
7 Hello World
!!:再一次执行上一条命令;
1 # cat test
2 Hello World
3 # !!
4 cat test
5 Hello World
!STRING:再一次执行命令历史列表中最近一个以指定的STRING开头的命令;
1 # !cat
2 cat test
3 Hello World
调用上一条命令的最后一个参数:
快捷键:ESC, .
Alt+.
!$:给出的字符组合
显示最近的n条件命令历史:
history #
控制命令历史的记录方式:
通过HISTCONTROL环境变量进行,其取值:
ignoredups:忽略重复的命令;重复是指连续且相同的令;
ignorespace:以空白字符开头的命令不记入历史;
ignoreboth:上述两者同时生效;
修改变量值的方式:
NAME='VALUE'
1 # echo $HISTCONTROL
2 ignoredups
3 # HISTCONTROL="ignoreboth"
4 # echo $HISTCONTROL
5 ignoreboth
c、bash特性之三:快捷键
Ctrl+a:跳至命令行首;
Ctrl+e:跳至命令行尾;
Ctrl+k:删除光标所在处至尾部的内容;
Ctrl+u:删除行首至光标所在处的内容;
d、bash的特性之四:命令补全和路径补全
命令补全:
shell程序在接收到用户执行命令的请求且分析完成之后,最左侧字符串将被当作命令去查找;
查找机制:
(1) 查找内部命令;
(2) 查找外部命令:
1、去$PATH变量所指定的各路径下,自左而右逐个搜索各目录下的文件名;
2、给定的打头的字符串如果能惟一标识某命令程序文件的文件名,则直接补全;
3、不能惟一标识,再击tab可给列表;
4、错误:没有任何命令可被此打头字符串标识;
路径补全:
在给定的起始路径的上级目录下,以对应路径下的打头字符串来逐一匹配上级目标下的每个文件:
惟一标识:tab补全;
不能惟一标识:tab, tab给出列表;
错误路径:没有响应;
e、bash的特性之五:命令行展开:
把命令行的给定的特殊符号自动替换为相应字符串的机制;
~: 自动替换为用户家目录;
~USERNAME:自动替换为指定用户的家目录;
{}:可承载一个以逗号分隔的路径列表,能够将其展开为多个独立路径;
例如:
/tmp/{a,b,c} /tmp/a /tmp/b /tmp/c
/tmp/{a,b}/z /tmp/a/z /tmp/b/z
1 # cd ~
2 # pwd
3 /root
4 # cd ~testuser
5 testuser]# pwd
6 /home/testuser
f、bash特性之六:命令的执行状态结果:
命令的正常输出结果:命令的返回值;
通过引用来保存下来或直接调用——“命令引用”
'COMMAND'
$(COMMAND)
1 # ls -ld 'pwd'
命令的执行状态结果:
成功:0
失败:1-255
1 # echo "Helllo World"
2 Helllo World
3 # echo $?
4 0
5 # echoa "Hello World"
6 -bash: echoa: command not found
7 # echo $?
8 127
bash用一个特殊变量来保存最一次执行的命令的状态结果:
$?
bash中的引用:
'':强引用
"":弱引用
“:命令引用
1 # echo '$PATH'
2 $PATH
3 # echo "$PATH"
4 /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin5 :/usr/bin:/root/bin
6 # echo 'pwd'
7 /home/testuser
g、bash的特性之七:glob
glob:文件名通配;快速引用多个文件;文件名整体匹配度检测;
元字符:基于元字符可编写匹配模式(pattern);
*:匹配任意长度的任意字符;
p,pa,,p,pa
p*:pa, p
?:匹配任意单个字符;
p?, p?a, p??
p??: pa, pad,
[ ]:匹配指定集合内的任意单个字符;
[a-z], [A-Z]:不区分字符大小写;
[0-9]
[a-z0-9]
[[:upper:]]:所有大写字母;
[[:lower:]]:所有小写字母;
[[:digit:]]:所有的数字;
[[:alpha:]]:所有字母;
[[:alnum:]]:所有字母和数字;
[[:space:]]:空白字符;
[[:punct:]]:标点符号;
[^ ]:匹配指定集合外的任意单个字符;
[^[:alpha:]]
测试:
1、显示/etc目录下,以非字母开头,后面跟了一个字母及其它任意长度任意字符的文件或目录;
ls -d /etc/[^[:alpha:]][a-z]*
2、复制/etc目录下,所以n开头,以非数字结尾的文件或目录至/tmp/etc目录下;
mkdir /tmp/etc
cp -r /etc/n*[^0-9] /tmp/etc/
3、显示/usr/share/man目录下,所有以man开头,后跟一个数字结尾的文件或目录;
ls -d /ur/share/man/man[0-9]
4、复制/etc目录下,所以p,m,r开头的,且以.conf结尾的文件或目录至/tmp/conf.d目录下;
mkdir /tmp/conf.d/
cp -r /etc/[pmr]*.conf /tmp/conf.d/
h、bash特性之八:变量
程序:指令+数据
数据:文件、变量;
变量:内存空间,有名称,名称即为变量名,对应的内存空间中的数据即为变量的值;
变量赋值:NAME=VALUE
=:赋值符号;
把VALUE存储到NAME指向的内存空间中;
编程语言:
强类型:严格区分变量中的数据类型;
弱类型:不区分变量中存储的数据类型,统一为字符型;
bash:统统默认为字符型数据;变量无需事先声明;
变量为什么有类型?
存储空间、存储格式、参与的运算、……
类型不同决定对数据的处理方式不同
变量命名:只能使用字母、数字和下划线;而且不能以数字开头;
变量名:见名知义;不能使用程序保留字,例如if、case、then、fi、esac、for、while、until、break、continue等等;
变量引用:${NAME}, $NAME
变量替换:把变量引用符号出现的位置替换为其指向的内存空间中的数据;
bash变量种类:
本地变量:作用域为当前shell进程;不包括其子进程;
环境变量:使用域为当前shell进程及其子进程;
局部变量
作用域:生效范围,也即可引用到的范围;
位置参数变量:
特殊变量
本地变量:
变量赋值:NAME=VALUE
变量引用:$NAME, ${NAME}
""
查看变量:set
撤销变量:unset NAME
注意:此处非为变量引用,因此不能使用$;
所有的本地变量在shell进程终止时,会被自动撤销;
环境变量:
变量声明和赋值:
declare -x NAME[=VALUE]
export NAME[=VALUE]
引用方式:
${NAME}, $NAME
注意:bash内嵌了许多环境变量,名称为全大写字母,例如UID、HOME、PWD、SHELL, PATH, HISTSIZE等等;
环境变量查看:
export, declare -x
env, printenv
撤销环境变量:
unset NAME
只读变量:常量
(1) declare -r NAME
(2) readonly NAME
不支持重新赋值,也不支持撤销操作;
1 # declare -r username='whoami'
2 # echo $username
3 root
4 # unset username
5 -bash: unset: username: cannot unset: readonly variable
i、bash基于特性之九:I/O重定向和管道
程序的数据流有三个:
输入数据流: <–,标准输入(stdin),键盘;
输出数据流:–>,标准输出(stdout), 显示器;
错误数据流:–>,错误输出(stderr),显示器;
fd:file descriptor,文件描述符;
stdin: 0
stdout: 1
stderr: 2
IO重定向:
输出重定向:
重定向程序正常执行的结果
COMMAND > /PATH/TO/SOMEFILE
覆盖重定向:覆盖目标文件中的原有内容;
COMMAND >> /PATH/TO/SOMEFILE
追加重定向:追加新产生的内容至目标文件尾部;
shell的一个功能开关:
set -C
禁止覆盖输出重定向至已存在的文件;
注意:此时仍然可以使用“>|”至目标文件;
set +C
关闭上述特性;
错误重定向:
重定向错误的执行结果;
COMMAND 2> /PATH/TO/SOMEFILE
错误输出覆盖重定向;
COMMAND 2>> /PATH/TO/SOMEFILE
错误输出追加重定向;
合并标准输出与错误输出流:
(1) &>, &>>
(2) COMMAND > /PATH/TO/SOMEFILE 2>&1
COMMAND >> /PATH/TO/SOMEFILE 2>&1
特殊输出目标:/dev/null
位桶:bit bucket
特殊的输入文件:/dev/zero
输入重定向:
COMMAND < /PATH/FROM/SOMEFILE
tr命令:把输出的数据当中的字符,实现对位转换,即把数据中的存在于字符集中的字符,统统转换为字符集2中对应的字符;
tr – translate or delete characters
tr [OPTION]… SET1 [SET2]
(1) tr SET1 SET2 < /PATH/FROM/SOMEFILE
字符转换
1 # cat test
2 Hello World
3 # cat test |tr 'a-z' 'A-Z'
4 HELLO WORLD
(2) tr -d SET1 < /PATH/FROM/SOMEFILE
删除
1 # cat test
2 Hello World
3 # cat test |tr -d 'ldH'
4 eo Wor
COMMAND << :
Here Document
用法:
COMMAND << EOF
COMMAND > /PATH/TO/SOMEFILE << EOF
管道:
COMMAND1 | COMMAND2 | COMMAND3 | …
练习1:把/etc/passwd文件最后三行信息中所有小写字符改为大写后输出;
1 # tail -3 /etc/passwd|tr 'a-z' 'A-Z'
2 TEST1:X:5002:5002::/HOME/TEST1:/BIN/BASH
3 TEST2:X:5003:5003::/HOME/TEST2:/BIN/BASH
4 TEST3:X:5004:5004::/HOME/TEST3:/BIN/BASH
练习2:取出/etc/fstab的第6行;
1 # head -6 /etc/fstab |tail -1
2 # Accessible filesystems, by reference, are maintained under '/dev/disk'
练习3:取出/etc目录下所有以p开头的文件或目录,只显示前5个;
1 # ls -d /etc/p*|head -5
2 /etc/pam.d
3 /etc/passwd
4 /etc/passwd-
5 /etc/pinforc
6 /etc/pkcs11
tee命令:
tee – read from standard input and write to standard output and files
tee [OPTION]… [FILE]…
-a:使用追加输出,而非覆盖;
COMMAND | tee /PATH/TO/SOMEFILE
1 # cat test|tee /tmp/test.cat
2 Hello World
3 # cat /tmp/test.cat
4 Hello World
shell的展开
花括号展开
在非引号内的内容,如果用花括号包括,而且里面用逗号分隔(至少包含一个逗号,可以是空内容),这样花括号里的内容会被展开成用空格分开的一个列表,花括号前后可以紧随前缀和后缀(前后缀都是可选的)。
例如:
echo {a,b,c}
echo hello,{world,pig}
echo rep{,,,,,}eat
注意花括号展开,前缀不能是$,因为${...}在shell中是变量
波浪号展开
从波浪号~到第一个未被引号包含的斜杠/(如果没有斜杠,则全部算上),作为波浪号前缀。
在波浪号后面的字符串作为一个可能的登录名:如果为空,被展开成该用户的HOME变量,如果HOME变量未设置,则用用户执行shell的主目录替换。如果不为空,则按照该登录名的主目录替换
例如:
echo ~ # 显示$HOME内容
HOME=/bin && echo ~ # 显示/bin
unset HOME && echo ~ # 显示当前用户主目录
echo ~root # 显示root用户主目录
波浪号还可以与加减号和数字,产生一个遍历文件夹堆栈的效果(关于文件夹堆栈,参考dirs、pushd、popd几个命令)。
echo ~+ # 显示$PWD
echo ~- # 显示$OLDPWD
echo ~+2 # 显示dirs中第3个内容,索引基于0
echo ~-3 # 显示dirs中倒数第4个内容,索引基于0
如果无法展开,那就会原样显示,例如dir堆栈中只有1个内容,那么+1是无法展开的(这时只有+0有效)。
Shell参数和变量展开
用$符号开始,后面接着变量名或者花括号括起来的变量名,如果是花括号内以叹号开头,那么就是变量名本身。
例如:
echo $PWD # 显示PWD对应的值
echo ${PWD} # 显示PWD对应的值
echo ${!PWD} # 显示“PWD”这个变量名,而不是它的值
echo ${!P*} # 显示所有以P开头的环境变量名
如果一个变量名不存在,就创建它。
echo ${HELLO:=hello} # 如果HELLO不存在,就用hello给它赋值,否则直接输出$HELLO的值
算术展开
放在$(( ))中的表达式会被计算,其中变量会被求值,例如:
a=1 && b=3 && echo $(($a+$b))
如果是数字,0开头的8进制,0x开头的16进制,其它进制用Base#number的方式
可支持2~64进制,如果进制小于等于36,可以用a-z或A-Z表示10-35,如果进制大于36,则a-z表示10-35,A-Z表示36-61,@表示62,表示63
例如:
echo $((16#32)) # 16进制的32,输出50
echo $((64#@)) # 输出4031 = 62 * 64 + 63
用$[]也可以算术展开,但是不要和测试条件[]混淆了
例如:
echo $[1+4]
文件名展开
进行字词分隔后,如果不指定-f选项,shell会搜索"*","?","[",如果遇到了,就会认为是一个带pattern的word,然后用字典序将符合的所有文件名替换过去,如果没有文件名匹配:1 shell的nullglob选项关闭,则不进行文件名展开,保留word原样;2 shell的nullglob打开,则移除这个word。如果shell的nocaseglob选项打开,则忽略大小写。
当匹配文件名时(这里指不包括文件夹),除非shell的dotglob被设置,否则.或./开头的文件都必须显示指定,例如:
ls * # 列出当前文件夹中所有不以"."开头的文件
ls .* # 列出当前文件夹中所有以"."开头的文件
当匹配文件名时,"/"必需显示匹配,例如:
ls ./* 和 ls .//是不同的。
其它情况下,"."和普通字符一样,例如:
.txt和txt都可以匹配a.txt
还有一个系统变量GLOBIGNORE,如果一个文件名匹配了一个pattern word,但是它也匹配了GLOBIGNORE,则它会被忽略,不过两个特殊文件一定会被忽略,就是"."和".."。
如果GLOBIGNORE打开,那么dotglob选项也会自动打开,这样会导致当你ls 时,其他以"."开头的文件也会被match,如果想忽略"."开头的文件,可以在GLOBIGNORE里面添加一个"."的匹配。如果GLOBIGNORE未设定,则dotglob关闭。