一看就懂的Linux Shell的基础使用(二)
脚本
bash不仅可以直接输入到命令行执行,也可以作为一种脚本语言。作为一种脚本语言,Bash也拥有变量、分支语句、循环语句等。
可以在某个文件里写bash脚本,然后运行它,不必每次都在命令行里输入。将以下代码保存到hello.sh
里,.sh
后缀代表这是一个bash脚本,当然,使用其他后缀也不影响脚本正常运行。
1 |
|
文件的第一行是#!/bin/bash
,以#
开始的内容是注释,意味着不会被运行。但是在文件的最开始,以#!
开头的注释有个特别的名字,它被称作“shebang”,可以提示系统,使用什么程序来解析下面的代码。这里的#!/bin/bash
,告诉操作系统使用/bin/bash
解释下面的代码,也可以写作#!/bin/sh
,因为/bin/sh
是一个指向bin/bash
的软链接。
在执行之前,请先向这个文件添加执行的权限,因为Linux默认的新文件权限是644
,即rw-r--r--
,任何人都无法执行。使用chmod +x hello.sh
来添加执行权限。
然后,有两种方式可以运行脚本,第一种是输入./hello.sh
,这里不能写成hello.sh
,因为对于这样的命令,Linux会在PATH变量中查找,而不从当前文件夹中查找。这种方式没有指定运行脚本的解释器,因此系统会读取Shebang,然后使用/bin/bash
解释代码
第二种方式就是指定解释器,将文件作为参数传入,输入sh hello.sh
,就可以看到一行Hello World!
。这种情况显式指定了使用sh
解释运行,因此shebang不会生效
变量
在bash中,给变量赋值使用foo=bar
,请注意,这里的等号前后不能加上任何空格,这是因为bash脚本会用空格来分割命令和命令的参数,如果输入foo = bar
,解释器会调用foo
命令,并传递=
和bar
在上一节中已经提到过了,bash中的字符串使用'
或"
包裹,但这两个记号的含义并不相同,前者会保留原始字符串,而后者会进行转义。
1 | cyrus:~$ foo=bar |
这里的$
代表引用相应的变量,但是在'
字符串内部并不会生效。
引用变量也可以写成${foo}
,和$foo
并无不同,但是${foo}
的写法可以更精确地界定变量名的范围,防止歧义。
数组
bash也支持定义数组,下标和其他大多数编程语言一样是从0开始的
1 | cyrus:~$ array=(a o e i u) |
(())
bash为了方便作为命令行使用,变量都是字符串类型的,因此命令foo=1+1
只会将字符串1+1
赋值给foo
,而不会计算出2
,要进行数学运算,应使用(())
双括号包裹,在双括号里引用变量是不需要加$
的。
1 | cyrus:~$ foo=1+1 |
可以使用的运算和C语言的语法是一致的:+
、-
、*
、/
(注意是整除,不支持小数)、%
、=
(、==
、!=
、<
、>
、foo++
、++foo
、foo--
、--foo
、!
、||
、&&
、~
、|
、&
、<<
、>>
、
另外还有一个符号#
,可以用来进制转换
1 | cyrus:~$ echo $((8#123)) |
两条命令分别计算出了 和 的10进制表示。
[]
还有一个语法是[ 表达式 ]
,但实际上这么说并不准确,因为这其实不是一个语法,[
是一个程序的名字,可以试着运行which [
,会发现[
其实是放在/usr/bin/[
的一个程序,它的运行效果和命令test
是一样的,具体使用细节可以输入man [
查询。
[
是一个程序,而]
只不过是传入的最后一个参数罢了(这个参数是必须的,否则会报错)。正因如此,必须写成[ 表达式 ]
,而不是[表达式]
。即表达式前后必须加空格
表达式 | 含义 |
---|---|
!表达式 | 逻辑非 |
表达式 -a 表达式 | 逻辑且 And |
表达式 -o 表达式 | 逻辑或 Or |
-n 字符串 | 字符串长度非0 Non-zero |
-z 字符串 | 字符串长度为0 Zero |
字符串 = 字符串 | 字符串相等 |
字符串 != 字符串 | 字符串不相等 |
字符串 > 字符串 | 字符串大于,需要转义,写成\> 而不是> |
字符串 < 字符串 | 字符串小于,同样需要转义,因为< 、> 都有特殊含义 |
( 表达式 ) | 就是括号的意思,表达式分组 |
整数 -eq 整数 | 整数相等 EQual to |
整数 -ge 整数 | 整数大于等于 Greater than or Equal to |
整数 -gt 整数 | 整数大于 Greater Than |
整数 -le 整数 | 整数小于等于 Less than or Equal to |
整数 -lt 整数 | 整数小于 Less Than |
整数 -nq 整数 | 整数小于 Not Equal to |
-f 文件 | 文件存在,并且类型为常规文件 |
-d 文件 | 文件存在,并且类型为目录 |
更多用法请输入man [
或man test
查看帮助文档。
还有一个指令是双方括号[[ 表达式 ]]
,双方括号除了支持以上特性,还支持了更多高级的特性,例如<
、>
、(
、)
不需要转义等。双方括号表达式不支持所有POSIX系统,但是大多数时候都是可用的,为了避免犯错,建议使用双方括号表达式。
定义函数
在bash中定义一个函数,它的作用是创建一个文件夹并进入,可以这样写:
1 | mcd () { |
这里的$1
是一个特殊的变量,代表传入的第一个参数,例如我们定义以上函数后,输入mcd example
,就会有以下效果:
1 | cyrus:~$ mcd () { |
像这样的特殊变量还有很多,以下是常用的一些:
$0
:脚本名$1
到$9
:脚本的参数。$1
是第一个参数,依此类推,超过第9个时,应该加上大括号,如${14}
是第14个参数。$@
:所有参数$#
:参数个数$?
:前一个命令的返回值$$
:当前脚本的进程ID(Process Identification, PID)!!
- 完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败(会出现Permission denied
)时,可以使用sudo !!
再尝试一次。$_
- 上一条命令的最后一个参数。
更多请参见这里
上面需要特殊解释的是$?
,它给出上一个命令的返回值,如果为0
,就意味着程序正常退出,如果非0,就代表程序异常退出。true
和false
,也是两个程序,其中true
永远返回0
,false
永远返回1
这个返回码可以搭配&&
、||
这两个运算符使用,它们都是短路运算符。
&&
其实是逻辑与运算,也可以用来连接两条命令,当前面的命令执行成功时才执行后面的命令。用&&
连接多个命令,假如中间发生了错误,就不会继续执行,引发一连串的错误。||
相应的,逻辑或运算是当前面的命令执行失败时才执行后面的命令。可以用于设置一个“Plan B”,当前面的命令执行失败,就执行“Plan B”。;
就是单纯的先后执行两条命令,无论成功与否,两条命令都会执行。
1 | false || echo "Oops, fail" |
?
、*
、{}
?
和*
分别可以匹配一个任意字符和多个任意字符,而{}
可以展开一个字符串列表,如:
1 | rm foo? # 会删除foo1、foo2、foob,不会删除foo、foo12 |
流程控制语句
if
bash中if
的格式是这样的:
1 | if 条件 |
或者写成一行(适用于在命令行界面下使用)
1 | if 条件; then 命令1; 命令2; ...; 命令n; fi |
if-else语法,和if-elif-else语法是类似的:
1 | if 条件 |
或
1 | if 条件; then 命令1; 命令2; else 命令3; 命令4; fi |
if-elif-else:
1 | if 条件1 |
或者
1 | if 条件1; then 命令1; elif 条件2; then 命令2; else 命令3; fi |
(这么复杂的语句还是写在脚本文件里吧,不建议写成一行了)
具体示例:
1 | compare() { |
运行脚本,输出
1 | 282 大于 14 |
for
for
循环语句的语法如下:
1 | for i in 列表项1 列表项2 ... 列表项n |
写成一行
1 | for i in 列表项1 列表项2 ... 列表项n; do 命令1; 命令2; ...; 命令n done |
示例
1 | foo=bar |
输出
1 | i is 1 now. |
while
while
的语法如下:
1 | while 条件 |
case
case
其实就是其他编程语言中的switch
语句,case
的语法比较奇怪,结束case
语句要使用case
这个单词反过来拼写的esac
,这个和if
->fi
是一样的。然后,每个子块里面需要用匹配式) 命令 ;;
的格式来写。
1 | echo '请选择 a~e 之间的一个英文字母' |
break
和continue
当然,bash也是支持break
和continue
的,它们的用法也和大多数编程语言一样。
用$()
组合命令
$()
可以获得一个命令的输出并用它替换,例如
1 | for i in $(ls); do |
输出
1 | 我有一个文件,它叫做Downloads |
find
find
指令可以用来查找文件,示例如下
1 | # 查找所有名称为src的文件夹 |
grep
grep
命令可以用来从一段文本里面查找想要的字符串,比如
1 | cyrus:~$ cat poem.txt |
alias
alias
命令可以创建别名
1 | # 创建常用命令的缩写 |
这些别名并不会持续生效,如果想要保存他们,需要在~/.bashrc
里面添加对应的配置,就可以每次启动bash的时候都加载这些配置。