创建及运行
sh 演变过程:
- 创建 .sh 结尾文件
- 头部指定脚本要使用的 Shell
!/bin/bash
- chmod +x 给该文件执行的权限
- ./test.sh 执行该文件
- bash -x test.sh: 使用调试模式运行shell,每一行命令系统都会给与相应的输出。
- 如果需要直接运行,跟系统命令一样
test.sh
直接运行,可以将 test.sh 拷贝到 /usr/bin 这个目录里sudo cp test.sh /usr/bin
,因为这个目录是在 PATH 系统变量中的,现在我们就可以直接运行 test.sh (不建议)
#!/bin/bash
ls
变量
定义变量
message='Hello World' 通过上面这句话,我们创建了一个变量 message (英语“信息,消息”的意思):
- 变量名是 message
- 变量值是 Hello World
注意:在等号两边不要加空格。Bash 的语法在不少地方是比较“吹毛求疵”
如果你要在变量的值中加入一个单引号( ' ),需要在前面加上反斜杠( \ )。事实上,单引号用于界定内容,所以要真的在内容里插入一个单引号,就须要在前面用一个特殊的符号来指明,这个特殊的符号就是反斜杠,也称为“转义字符”,或简称“转义符”。例如:
message='Hello, it\'s me'
使用变量
#!/bin/bash
message='Hello World'
echo $message
shell对引号的处理
单引号
单引号忽略被它括起来的所有特殊字符。
#!/bin/bash
message='Hello World'
echo 'The message is $message'
如果变量被包含在单引号里面,那么变量不会被解析,美元符号( message`
双引号
不同于单引号忽略所有特殊字符,双引号忽略大多数特殊字符,但不包括:美元符号( $ )、反引号( ` )、反斜杠( \ ),这 3 种特殊字符将不被忽略。 不忽略美元符号意味着 Shell 在双引号内部可进行变量名替换。
反引号
反引号要求 Shell 执行被它括起来的内容。 什么意思呢?我们来看一个例子你就懂了:
#!/bin/bash
message=`pwd`
echo "You are in the directory $message"
运行这个脚本,显示:You are in the directory /home/oscar
read 请求用户输入
我们可以请求用户输入文本,这就要用到 read 命令了。 read 是英语“阅读,读取”的意思。read 命令读取到的文本会立即被储存在一个变量里。 read 命令有好些有趣的用法。首先,最简单的当然是直接后接一个变量名,这样用户输入的文本就会被储存在这个 变量中。 我们来写一个例子:
#!/bin/bash
echo "input what you wan\'t to see"
read name
echo "$name"
运行后如下:
read 同时给几个变量赋值
#!/bin/bash
read firstname lastname
echo "Hello $firstname $lastname !"
read 命令一个单词一个单词(单词是用空格分开的)地读取你输入的参数,并且把每个参数赋值给对应变量。 这样我们输入的名和姓就分别被赋值给了 firstname(英语“名字”的意思)和 lastname(英语“姓氏”的意思)这两个变量。 如果你输入了比预期更多的参数,比如三个,四个,甚至更多,那么最后一个变量就会把多出来的参数全部拿走。
-p: 显示提示信息给用户
#!/bin/bash
read -p 'Please enter your name : ' name
echo "Hello $name !"
-n 限制输入数量
#!/bin/bash
read -p 'Please enter your name (5 characters max) : ' -n 5 name
echo "\nHello $name !"
如果用户输出超过5个字符,会直接打印
-t 限制输入时间
#!/bin/bash
read -p 'Please enter the code to defuse the bomb (you have 5 seconds) : ' -t 5 code
echo -e "\nBoom !"
-s :隐藏输入内容
#!/bin/bash
read -p 'Please enter your password : ' -s password
echo -e "\nThanks, I will tell everyone that your password is $password"
数学运算
Bash 本身不会操纵数字,因此它也不会做运算。我们需要用到 let 命令。
#!/bin/bash
let "a = 5"
let "b = 2"
let "c = a + b"
echo "c = $c"
可以用到的运算符有以下:
运算符 | 符号 |
---|---|
加法 | + |
减法 | - |
乘法 | * |
除法 | / |
幂(乘方) | ** |
余(整数除法的余数) | % |
和其他大多数主流编程语言一样,Bash 也支持运算的连写
let "a = a * 3"
let "a *= 3"
脚本参数
Shell 脚本也可以接收参数。
假设,我们可以这样调用我们的脚本文件:
./variable.sh 参数1 参数2 参数3 ...
这些个 参数1,参数2,参数3 … 被称为“参数变量。
- $# :包含参数的数目。
- $0 :包含被运行的脚本的名称 (我们的示例中就是 variable.sh )。
- $1 :包含第一个参数。
- $2 :包含第二个参数。
- …
- $8 :包含第八个参数。 如果我们有很多很多参数怎么办呢?可以用 shift 命令来“挪移”参数,以便依次处理。 shift 是英语“移动,移位”的意思。 什么意思呢?我们来看例子:
#!/bin/bash
echo "The first parameter is $1"
shift
echo "The first parameter is now $1"
可以看到,在调用 shift 命令后, 2 对应了第三个参数,以此类推。
数组
- 定义:
array=('value0' 'value1' 'value2')
- 如果要访问其中一个格子的内容,要用到这样的语法:
${array[2]}
- 单独给数组的元素赋值,例如:
array[3]='value3'
- 一次性显示数组中所有的元素值,需要用到通配符 * (星号)。
array[*]
例子:
#!/bin/bash
array=('value0' 'value1' 'value2')
array[5]='value5'
echo ${array[*]}
条件语句
- 基本格式:
if [ 条件测试 ]
then
做这个
fi
注意:方括号 [] 中的 条件测试 两边必须要空一格。不能写成 [test] ,而要写成 [ test ] 。 2. 另一种写法:
if [ 条件测试 ]; then
做这个
fi
用这种写法时,在 if 条件判断和 then 之间要加一个分号。
例子:
#!/bin/bash
name="Enming"
if [ $name = "Enming" ]
then
echo "Hello $name !"
fi
- else : 否则
if [ 条件测试 ]
then
做这个
else
做那个
fi
- elif : 否则,如果 elif 是 else if 的缩写,表示“否则 - 如果”。 if, elif 和 else 三者配合的逻辑是这样的:
if [ 条件测试 1 ]
then
做事情 1
elif [ 条件测试 2 ]
then
做事情 2
elif [ 条件测试 3 ]
then
做事情 3
else
做其他事情
fi
我们来看一个例子:
#!/bin/bash
if [ $1 = "Matthew" ]
then
echo "Hello Matthew !"
elif [ $1 = "Mark" ]
then
echo "Hello Mark !"
elif [ $1 = "Luke" ]
then
echo "Hello Luke !"
elif [ $1 = "John" ]
then
echo "Hello John !"
else
echo "Sorry, I do not know you."
fi
条件测试
判断字符串
条件 | 意义 |
---|---|
string2 | 字符串是否相等(大小写敏感) |
string2 | 字符串是否不同 |
-z $string | 字符串 string 是否为空。z 是 zero 的首字母,是英语“零”的意思。 |
-n $string | 字符串 string 是否不为空。n 是英语 not 的首字母,是英语“不”的意思。 |
判断数字
条件 | 意义 |
---|---|
num2 | 两个数字是否相等。和判断字符串所用的符号( = )不一样。eq 是 equal 的缩写,是英语“等于”的意思。 |
num2 | 两个数字是否不同。ne 是 not equal 的缩写,是英语“不等于”的意思。 |
num2 | 数字 num1 是否小于 num2。lt 是 lower than 的缩写,是英语“小于”的意思。 |
num2 | 数字 num1 是否小于或等于 num2。le 是 lower or equal 的缩写,是英语“小于或等于”的意思。 |
num2 | 数字 num1 是否大于 num2。gt 是 greater than 的缩写,是英语“大于”的意思。 |
num2 | 数字 num1 是否大于或等于 num2。ge 是 greater or equal 的缩写,是英语“大于或等于”的意思。 |
判断文件
条件 | 意义 |
---|---|
-e $file | 文件是否存在。e 是 exist 的首字母,表示“存在”。 |
-d $file | 文件是否是一个目录。因为 Linux 中一切都是文件,目录也是文件的一种。d 是 directory 的首字母,表示“目录”。 |
-f $file | 文件是否是一个文件。f 是 file 的首字母,表示“文件”。 |
-L $file | 文件是否是一个符号链接文件。L 是 link 的首字母,表示“链接”。 |
-r $file | 文件是否可读。r 是 readable 的首字母,表示“可读的”。 |
-w $file | 文件是否可写。w 是 writable 的首字母,表示“可写的”。 |
-x $file | 文件是否可执行。x 是 executable 的首字母,表示“可执行的”。 |
$file1 -nt | $file2 文件 file1 是否比 file2 更新。nt 是 newer than 的缩写,表示“更新的”。 |
file2 | 文件 file1 是否比 file2 更旧。ot 是 older than 的缩写,表示“更旧的”。 |
一次测试多个条件
符号 | 意义 |
---|---|
&& | 两个 &。表示“逻辑与”。此符号两端的条件必须全为真,整个条件测试才为真;只要有一个不为真,整个条件测试为假。 |
II | 两个竖线。表示“逻辑或”。此符号两端的条件只要有一个为真,整个条件测试就为真;只有两个都为假,整个条件测试才为假。 |
! | 非 |
case 语句
#!/bin/bash
#!/bin/bash
case $1 in
"Matthew")
echo "Hello Matthew !"
;;
"Mark")
echo "Hello Mark !"
;;
"Luke")
echo "Hello Luke !"
;;
"John")
echo "Hello John !"
;;
*)
echo "Sorry, I do not know you."
;;
esac
来分析一下上面的程序,因为有很多新的内容:
- case 1 表示我们要测试的变量是输入的第一个参数。in 是英语“在…之中”的意思。"Matthew") :测试其中一个 case,也就是 $1 是否等于 "Matthew" 。当然,我们也可以用星号来做通配符来匹配多个字符,例如 "M*") 可以匹配所有以 M 开头的字符串。
- ;; :类似于主流编程语言中的 break; ,表示结束 case 的读取,程序跳转到 esac 后面执行。
- *) :相当于 if 条件语句的 else,表示“否则”,就是“假如不等于上面任何一种情况”。
- esac :是 case 的反写,表示 case 语句的结束。
我们也可以在 case 语句的匹配项中做“或”的匹配,但是不是用两个竖线(逻辑或)了,而是用一个竖线。
#!/bin/bash
case $1 in
"dog" | "cat" | "pig")
echo "It is a mammal"
;;
"pigeon" | "swallow")
echo "It is a bird"
;;
*)
echo "I do not know what it is"
;;
esac
循环
while循环
while [ 条件测试 ]
do
做某些事
done
while [ 条件测试 ]; do
做某些事
done
例子:
#!/bin/bash
while [ -z $response ] || [ $response != 'yes' ]
do
read -p 'Say yes : ' response
done
util循环
#!/bin/bash
until [ "$response" = 'yes' ]
do
read -p 'Say yes : ' response
done
for循环
- for 循环可以遍历一个“取值列表”,基本的逻辑如下:
for 变量 in '值1' '值2' '值3' ... '值n'
do
做某些事
done
例子:
#!/bin/bash
for animal in 'dog' 'cat' 'pig'
do
echo "Animal being analyzed : $animal"
done
#!/bin/bash
for file in `ls`
do
echo "File found : $file"
done
更常规的 for 循环
#!/bin/bash
for i in `seq 1 10`
do
echo $i
done
修改默认的间隔:
for i in `seq 2 2 10`
do
echo $i
done
函数
- 定义:
函数名 () {
函数体
}
或者
function 函数名 {
函数体
}
例子:
#!/bin/bash
print_something(){
echo "hello,xiaohan"
}
print_something
print_something
- 传递参数 在 Shell 函数中,我们给它传递参数的方式其实很像给 Shell 脚本传递命令行参数。我们把参数直接置于函数名字后面,然后就像我们之前 Shell 脚本的参数那样: 2 , $3 等等。
print_something(){
echo "hello,xiaohan$1"
}
print_something D
print_something D
- 返回值 大多数主流编程语言都有函数返回值的概念,可以让函数回传一些数据。 Shell 的函数却没办法做到。但是 Shell 的函数可以返回一个状态,有点类似一个程序或命令退出时会有一个退出状态,表明是否成功。 Shell 函数要返回状态,也用 return 这个关键字( return 是英语“返回”的意思)。 例子:
#!/bin/bash
print_something () {
echo Hello $1
return 1
}
print_something Luke
print_something John
echo Return value of previous function is $?
- 第 5 行:返回的状态不一定要是被硬编码的(比如上例中的 1 ),也可以是一个变量。
- 第 10 行:变量 $? 包含前一次被运行的命令或函数的返回状态。一般来说,返回状态 0 表示一切顺利;一个非零值表示有错误。 如果你实在想要函数返回一个数值(比如说一个计算的值),那么你也可以用命令的执行结果。 如下:
#!/bin/bash
lines_in_file () {
cat $1 | wc -l
}
line_num=$(lines_in_file $1)
echo The file $1 has $line_num lines
变量
默认来说,一个变量是“全局的”(global),意味着在脚本的任何地方都可以访问它。我们也可以创建局部(local)变量。当我们在函数中创建局部变量时,这个变量就只能在这个函数中被访问。要定义一个局部变量,我们只要在第一次给这个变量赋值时在变量名前加上关键字 local 即可( local 是英语“本地 的”的意思)。定义局部变量有一个好处,就是可以防止被脚本的其它地方的代码意外改变数值。 例子:
#!/bin/bash
local_global () {
local var1='local 1'
echo Inside function: var1 is $var1 : var2 is $var2
var1='changed again' # 这里的 var1 是函数中定义的局部变量
var2='2 changed again' # 这里的 var2 是函数外定义的全局变量
}
var1='global 1'
var2='global 2'
echo Before function call: var1 is $var1 : var2 is $var2
local_global
echo After function call: var1 is $var1 : var2 is $var2
重载命令
我们可以用函数来实现命令的重载,也就是说把函数的名字取成与我们通常在命令行用的命令相同的名字。 例如,也许我们每次在脚本中调用 ls 命令时,其实是想要实现 ls -lh 的效果。那么我们可以这么做:
#!/bin/bash
ls () {
command ls -lh
}
ls
第 4 行如果没有 command 这个关键字(command 是英语“命令”的意思),那么程序会陷入无限循环。 如果你不小心忘了 command 关键字而陷入无限循环,可以用 Ctrl + c 的组合快捷键来停止程序。