Bash入门
Bash入门
什么是bash
Bash 是 Unix 系统和 Linux 系统的一种 Shell(命令行环境),是目前绝大多数 Linux 发行版的默认 Shell
什么是shell
学习 Bash,首先需要理解 Shell 是什么。Shell 这个单词的原意是“外壳”,跟 kernel(内核)相对应,比喻内核外面的一层,即用户跟内核交互的对话界面。
首先,Shell 是一个程序,提供一个与用户对话的环境。这个环境只有一个命令提示符,让用户从键盘输入命令,所以又称为命令行环境(command line interface,简写为 CLI)。Shell 接收到用户输入的命令,将命令送入操作系统执行,并将结果返回给用户。
查看当前使用是shell是什么
echo $SHELL |
当前正在使用的 Shell 不一定是默认 Shell,一般来说,ps
命令结果的倒数第二行是当前 Shell。
ps |
查看当前所有安装的Shell
cat /etc/shells |
退出和进入bash
# 进入bash |
bash基础
echo命令
echo命令可以在输入一行文本
echo hello world |
-n
参数
默认情况下,echo
输出的文本末尾会有一个回车符。-n
参数可以取消末尾的回车符,使得下一个提示符紧跟在输出内容的后面
-e
参数
-e
参数会解释引号(双引号和单引号)里面的特殊字符(比如换行符\n
)。如果不使用-e
参数,即默认情况下,引号会让特殊字符变成普通字符,echo
不解释它们,原样输出
$ echo "Hello\nWorld" |
bash命令格式
$ command [ arg1 ... [ argN ]] |
分号
分号(;
)是命令的结束符,使得一行可以放置多个命令,上一个命令执行结束后,再执行第二个命令
命令的组合符&&
和||
Command1 && Command2 |
上面命令的意思是,如果Command1
命令运行成功,则继续运行Command2
命令。
Command1 || Command2 |
上面命令的意思是,如果Command1
命令运行失败,则继续运行Command2
命令。
快捷键
Ctrl + L
:清除屏幕并将当前行移到页面顶部。Ctrl + C
:中止当前正在执行的命令。Shift + PageUp
:向上滚动。Shift + PageDown
:向下滚动。Ctrl + U
:从光标位置删除到行首。Ctrl + K
:从光标位置删除到行尾。Ctrl + W
:删除光标位置前一个单词。Ctrl + D
:关闭 Shell 会话。↑
,↓
:浏览已执行命令的历史记录。
模式拓展
关闭拓展
$ set -o noglob |
重新打开拓展
$ set +o noglob |
?
字符拓展
?
字符代表文件路径里面的任意单个字符,不包括空字符。比如,Data???
匹配所有Data
后面跟着三个字符的文件名。
# 存在文件 a.txt 和 b.txt |
上面命令中,?
表示单个字符,所以会同时匹配a.txt
和b.txt
。
如果匹配多个字符,就需要多个?
连用。
# 存在文件 a.txt、b.txt 和 ab.txt |
上面命令中,??
匹配了两个字符。
?
字符扩展属于文件名扩展,只有文件确实存在的前提下,才会发生扩展。如果文件不存在,扩展就不会发生。
*
字符扩展
*
字符代表文件路径里面的任意数量的任意字符,包括零个字符。
# 存在文件 a.txt、b.txt 和 ab.txt |
上面例子中,*.txt
代表后缀名为.txt
的所有文件。
如果想输出当前目录的所有文件,直接用*
即可。
$ ls * |
*
可以匹配空字符,下面是一个例子。
# 存在文件 a.txt、b.txt 和 ab.txt |
注意,*
不会匹配隐藏文件(以.
开头的文件),即ls *
不会输出隐藏文件。
如果要匹配隐藏文件,需要写成.*
如果要匹配隐藏文件,同时要排除.
和..
这两个特殊的隐藏文件,可以与方括号扩展结合使用,写成.[!.]*
。
$ echo .[!.]* |
[…]拓展
# 存在文件 a.txt 和 b.txt |
[start-end] 扩展
# 存在文件 a.txt、b.txt 和 c.txt |
长用例子:
[a-z]
:所有小写字母。[a-zA-Z]
:所有小写字母与大写字母。[a-zA-Z0-9]
:所有小写字母、大写字母与数字。[abc]*
:所有以a
、b
、c
字符之一开头的文件名。program.[co]
:文件program.c
与文件program.o
。BACKUP.[0-9][0-9][0-9]
:所有以BACKUP.
开头,后面是三个数字的文件名。
{start..end} 扩展
# 允许逆序 |
变量扩展
Bash 将美元符号$
开头的词元视为变量
字符类
[[:class:]]
表示一个字符类,扩展成某一类特定字符之中的一个。常用的字符类如下。
[[:alnum:]]
:匹配任意英文字母与数字[[:alpha:]]
:匹配任意英文字母[[:blank:]]
:空格和 Tab 键。[[:cntrl:]]
:ASCII 码 0-31 的不可打印字符。[[:digit:]]
:匹配任意数字 0-9。[[:graph:]]
:A-Z、a-z、0-9 和标点符号。[[:lower:]]
:匹配任意小写字母 a-z。[[:print:]]
:ASCII 码 32-127 的可打印字符。[[:punct:]]
:标点符号(除了 A-Z、a-z、0-9 的可打印字符)。[[:space:]]
:空格、Tab、LF(10)、VT(11)、FF(12)、CR(13)。[[:upper:]]
:匹配任意大写字母 A-Z。[[:xdigit:]]
:16进制字符(A-F、a-f、0-9)
变量
显示所有变量
通过set可以显示所有变量
创建变量
a=z # 变量 a 赋值为字符串 z |
读取变量
$ foo=bar |
删除变量
unset NAME |
输出变量,export 命令
用户创建的变量仅可用于当前 Shell,子 Shell 默认读取不到父 Shell 定义的变量。为了把变量传递给子 Shell,需要使用export
命令。这样输出的变量,对于子 Shell 来说就是环境变量。
declare 命令
declare
命令可以声明一些特殊类型的变量,为变量设置一些限制,比如声明只读类型的变量和整数类型的变量。
它的语法形式如下。
declare OPTION VARIABLE=value |
declare
命令的主要参数(OPTION)如下。
-a
:声明数组变量。-f
:输出所有函数定义。-F
:输出所有函数名。-i
:声明整数变量。-l
:声明变量为小写字母。-p
:查看变量信息。-r
:声明只读变量。-u
:声明变量为大写字母。-x
:该变量输出为环境变量。
declare
命令如果用在函数中,声明的变量只在函数内部有效,等同于local
命令。
不带任何参数时,declare
命令输出当前环境的所有变量,包括函数在内,等同于不带有任何参数的set
命令。
let 命令
let
命令声明变量时,可以直接执行算术表达式。
$ let foo=1+2 |
上面例子中,let
命令可以直接计算1 + 2
。
let
命令的参数表达式如果包含空格,就需要使用引号。
$ let "foo = 1 + 2" |
字符串操作
获取字符串长度
${#varname} |
提取子字符串
${varname:offset:length} |
搜索和替换
# 如果 pattern 匹配变量 variable 的开头, |
改变大小写
# 转为大写 |
算术运算
((...))
会自动忽略内部的空格,所以下面的写法都正确,得到同样的结果。
$ ((foo = 5 + 5)) |
进制数
Bash 的数值默认都是十进制,但是在算术表达式中,也可以使用其他进制。
number
:没有任何特殊表示法的数字是十进制数(以10为底)。0number
:八进制数。0xnumber
:十六进制数。base#number
:base
进制的数。
位运算
$((...))
支持以下的二进制位运算符。
<<
:位左移运算,把一个数字的所有位向左移动指定的位。>>
:位右移运算,把一个数字的所有位向右移动指定的位。&
:位的“与”运算,对两个数字的所有位执行一个AND
操作。|
:位的“或”运算,对两个数字的所有位执行一个OR
操作。~
:位的“否”运算,对一个数字的所有位取反。^
:位的异或运算(exclusive or),对两个数字的所有位执行一个异或操作。
逻辑运算
$((...))
支持以下的逻辑运算符。
<
:小于>
:大于<=
:小于或相等>=
:大于或相等==
:相等!=
:不相等&&
:逻辑与||
:逻辑或!
:逻辑否expr1?expr2:expr3
:三元条件运算符。若表达式expr1
的计算结果为非零值(算术真),则执行表达式expr2
,否则执行表达式expr3
。
如果逻辑表达式为真,返回1
,否则返回0
expr 命令
expr
命令支持算术运算,可以不使用((...))
语法。
expr
命令也不支持非整数参数。
let 命令
let
命令用于将算术运算的结果,赋予一个变量。
let x=2+3 |
脚本入门
Shebang行
脚本的第一行通常是指定解释器,即这个脚本必须通过什么解释器执行。这一行以#!
字符开头,这个字符称为 Shebang,所以这一行就叫做 Shebang 行。
#!
后面就是脚本解释器的位置,Bash 脚本的解释器一般是/bin/sh
或/bin/bash
。
|
#!
与脚本解释器之间有没有空格,都是可以的。
如果 Bash 解释器不放在目录/bin
,脚本就无法执行了。为了保险,可以写成下面这样。
执行权限和路径
# 给所有用户执行权限 |
脚本参数
调用脚本的时候,脚本文件名后面可以带有参数。
$ script.sh word1 word2 word3 |
上面例子中,script.sh
是一个脚本文件,word1
、word2
和word3
是三个参数。
脚本文件内部,可以使用特殊变量,引用这些参数。
$0
:脚本文件名,即script.sh
。$1
~`$9`:对应脚本的第一个参数到第九个参数。$#
:参数的总数。$@
:全部的参数,参数之间使用空格分隔。$*
:全部的参数,参数之间使用变量$IFS
值的第一个字符分隔,默认为空格,但是可以自定义
|
shift 命令
shift
命令可以改变脚本参数,每次执行都会移除脚本当前的第一个参数($1
),使得后面的参数向前一位,即$2
变成$1
、$3
变成$2
、$4
变成$3
,以此类推。
while
循环结合shift
命令,也可以读取每一个参数。
|
exit命令
exit
命令用于终止当前脚本的执行,并向 Shell 返回一个退出值。
# 退出值为0(成功) |
命令执行结果
命令执行结束后,会有一个返回值。0
表示执行成功,非0
(通常是1
)表示执行失败。环境变量$?
可以读取前一个命令的返回
cd /path/to/somewhere |
source 命令
source
命令用于执行一个脚本,通常用于重新加载一个配置文件。
别名alias命令
unalias
解除别名
alias
命令用来为一个命令指定别名,这样更便于记忆。下面是alias
的格式。
alias NAME=DEFINITION |
read命令
有时,脚本需要在执行过程中,由用户提供一部分数据,这时可以使用read
命令。它将用户的输入存入一个变量,方便后面的代码使用。用户按下回车键,就表示输入结束。
(1)-t 参数
read
命令的-t
参数,设置了超时的秒数。如果超过了指定时间,用户仍然没有输入,脚本将放弃等待,继续向下执行。
|
环境变量TMOUT
也可以起到同样作用,指定read
命令等待用户输入的时间(单位为秒)
2)-p 参数
-p
参数指定用户输入的提示信息。
read -p "Enter one or more values > " |
3)-a 参数
-a
参数把用户的输入赋值给一个数组,从零号位置开始。
4)-n 参数
-n
参数指定只读取若干个字符作为变量值,而不是整行读取。
5)-e 参数
-e
参数允许用户输入的时候,使用readline
库提供的快捷键,比如自动补全
6)其他参数
-d delimiter
:定义字符串delimiter
的第一个字符作为用户输入的结束,而不是一个换行符。-r
:raw 模式,表示不把用户输入的反斜杠字符解释为转义字符。-s
:使得用户的输入不显示在屏幕上,这常常用于输入密码或保密信息。-u fd
:使用文件描述符fd
作为输入
条件判断
if test $USER = "foo"; then |
test命令
if
结构的判断条件,一般使用test
命令,有三种形式。
# 写法一 |
文件判断
[ -a file ]
:如果 file 存在,则为true
。[ -b file ]
:如果 file 存在并且是一个块(设备)文件,则为true
。[ -c file ]
:如果 file 存在并且是一个字符(设备)文件,则为true
。[ -d file ]
:如果 file 存在并且是一个目录,则为true
。[ -e file ]
:如果 file 存在,则为true
。[ -f file ]
:如果 file 存在并且是一个普通文件,则为true
。[ -g file ]
:如果 file 存在并且设置了组 ID,则为true
。[ -G file ]
:如果 file 存在并且属于有效的组 ID,则为true
。[ -h file ]
:如果 file 存在并且是符号链接,则为true
。[ -k file ]
:如果 file 存在并且设置了它的“sticky bit”,则为true
。[ -L file ]
:如果 file 存在并且是一个符号链接,则为true
。[ -N file ]
:如果 file 存在并且自上次读取后已被修改,则为true
。[ -O file ]
:如果 file 存在并且属于有效的用户 ID,则为true
。[ -p file ]
:如果 file 存在并且是一个命名管道,则为true
。[ -r file ]
:如果 file 存在并且可读(当前用户有可读权限),则为true
。[ -s file ]
:如果 file 存在且其长度大于零,则为true
。[ -S file ]
:如果 file 存在且是一个网络 socket,则为true
。[ -t fd ]
:如果 fd 是一个文件描述符,并且重定向到终端,则为true
。 这可以用来判断是否重定向了标准输入/输出/错误。[ -u file ]
:如果 file 存在并且设置了 setuid 位,则为true
。[ -w file ]
:如果 file 存在并且可写(当前用户拥有可写权限),则为true
。[ -x file ]
:如果 file 存在并且可执行(有效用户有执行/搜索权限),则为true
。[ FILE1 -nt FILE2 ]
:如果 FILE1 比 FILE2 的更新时间更近,或者 FILE1 存在而 FILE2 不存在,则为true
。[ FILE1 -ot FILE2 ]
:如果 FILE1 比 FILE2 的更新时间更旧,或者 FILE2 存在而 FILE1 不存在,则为true
。[ FILE1 -ef FILE2 ]
:如果 FILE1 和 FILE2 引用相同的设备和 inode 编号,则为true
。
字符串判断
[ string ]
:如果string
不为空(长度大于0),则判断为真。[ -n string ]
:如果字符串string
的长度大于零,则判断为真。[ -z string ]
:如果字符串string
的长度为零,则判断为真。[ string1 = string2 ]
:如果string1
和string2
相同,则判断为真。[ string1 == string2 ]
等同于[ string1 = string2 ]
。[ string1 != string2 ]
:如果string1
和string2
不相同,则判断为真。[ string1 '>' string2 ]
:如果按照字典顺序string1
排列在string2
之后,则判断为真。[ string1 '<' string2 ]
:如果按照字典顺序string1
排列在string2
之前,则判断为真。
注意,test
命令内部的>
和<
,必须用引号引起来(或者是用反斜杠转义)。否则,它们会被 shell 解释为重定向操作符。
整数判断
[ integer1 -eq integer2 ]
:如果integer1
等于integer2
,则为true
。[ integer1 -ne integer2 ]
:如果integer1
不等于integer2
,则为true
。[ integer1 -le integer2 ]
:如果integer1
小于或等于integer2
,则为true
。[ integer1 -lt integer2 ]
:如果integer1
小于integer2
,则为true
。[ integer1 -ge integer2 ]
:如果integer1
大于或等于integer2
,则为true
。[ integer1 -gt integer2 ]
:如果integer1
大于integer2
,则为true
。
正则判断
[[ string1 =~ regex ]] |
循环
while循环
while condition; do |
until
until
循环与while
循环恰好相反,只要不符合判断条件(判断条件失败),就不断循环执行指定的语句。一旦符合判断条件,就退出循环
until condition; do |
for…in 循环
for variable in list |
break,continue
Bash 提供了两个内部命令break
和continue
,用来在循环内部跳出循环。
break
命令立即终止循环,程序继续执行循环块之后的语句,即不再执行剩下的循环
函数
函数(function)是可以重复使用的代码片段,有利于代码的复用。它与别名(alias)的区别是,别名只适合封装简单的单个命令,函数则可以封装复杂的多行命令
# 第一种 |
#删除一个函数,可以使用unset命令。 |
数组
#创建数组 |
Set命令
# 执行脚本时,如果遇到不存在的变量,Bash 默认忽略它。 |
Bash 的错误处理
# 写法一 |