bash是目前应用最多的shell脚本,由命令和注释组成,注释跟在井号#
后面。
脚本第一行指出由哪种程序来解析脚本,这一行称为shbang
行,必须顶端第一行。如下有两种方式来指定程序位置,第一种直接指定bash
程序位置,第二种通过env
程序查找bash
位置,可用于bash
不是安装在标准位置的情况,具有更高的灵活性。
#!/bin/bash
#!/bin/env bash
bash
变量名区分大小写,首字符不能为数字。根据作用范围可分为局部变量和环境变量。
local
声明局部变量。HOME
、PATH
等#定义变量
VAR_NAME=value
#只读变量
readonly VAR_NAME=value
#定义环境变量
export VAR_NAME=value
#变量引用
$name
${name}
#删除变量
unset name
#变量拼接
aaa=${bbb}ccc
PS: 定义变量和赋值时等号前后都不能有空格
Linux命令的输出也可以被赋给一个变量,可以通过反引号引用命令或将命令包含在美元符号开始的一对圆括号。
variable_name=`COMMAND`
variable_name=$(COMMAND)
#示例
> TODAY=`date +%F`
> echo "Today is $TODAY"
Today is 2018-03-21
> MY_PWD=$(pwd)
> echo ${MY_PWD}
/home/bob
PS:shell会对双引号” “内的美元符号后的变量执行变量扩展,对单引号’ ‘则不会。在很多地方有类似的效果,所有不想shell自动扩展时可以使用单引号。
用户可以通过命令行向脚本传递参数,我们可以用位置参量来引用。
$0 脚本名(脚本路径)
$1...${10} 单独的位置参量
$# 位置参量个数
$* 所有参数,全部参数合为一个字符串
$@ 所有参数,全部参数分为单独的字符串;最后两个只有在加双引号时才有区别
$? 上个命令执行的结果,经常用到
带参数的set命令将重置位置变量,且原来的参量列表就丢失了。要清除所有位置参量,可使用set -,$0始终代表脚本名。
示例:
#!/bin/bash
echo "===input three param"
echo $1
echo $2
echo $3
echo '===foreach $*'
for i in $*;do
echo $i
done
echo '===foreach $@'
for i in $@;do
echo $i
done
echo '===foreach "$*"'
for i in "$*";do
echo $i
done
echo '===foreach "$@"'
for i in "$@";do
echo $i
done
echo "=== set"
set 1 2 3
echo $1
echo $2
echo $3
set -
echo \$1 is $1
结果:
# ./test.sh "blue sky" green red
===input three param
blue sky
green
red
===foreach $*
blue
sky
green
red
===foreach $@
blue
sky
green
red
===foreach "$*"
blue sky green red
===foreach "$@"
blue sky
green
red
===set
1
2
3
$1 is 1
bash支持一维数组,不支持多维数组。
#定义
array_name=(value1 ... valuen)
#读取数组
${array_name[index]}
#赋值
array_name[index]=value
#获取数组中所有元素
${array_name[*]}
${array_name[@]}
#数组元素个数
${#array_name[*]}
${#array_name[@]}
if为最简单的条件语句,if后跟一个Linux
命令或测试表达式作为条件,如果推出状态为0,即命令正常执行,则为true;如果推出状态非0,则为false。
#单分支
if condition;then
COMMANDS(命令组)
fi
#双分支
if condition;then
COMMANDS
else
COMMANDS
fi
#多分支
if condition;then
COMMANDS
elif condition2;then
COMMANDS
...
else
COMMANDS
fi
条件condition可以是命令,test测试表达式,中括号[ ],双中括号[[ ]],或双括号的算术表达式(( ))。then可以另起一行,那样条件表达式后面的分号就可以省略。
test测试表达式和中括号[ ]等效,[[ ]]相对于前两个测试表达式会有更好的提升,比如其中的字符不会转义,支持pattern matching等。总体来说双中括号会优于单中括号。双括号(( ))称为算数表达式,在其中可以使用与C相同算术的语法,具体参见链接。
字符 | 说明 |
---|---|
-lt | 小于 |
-gt | 大于 |
-eq | 相等 |
-ne | 不等 |
-le | 小于等于 |
-ge | 大于等于 |
a=1
b=2
if [ $a -lt $b ];then
echo "a < b"
fi
if [ $a -ne 3 ];then
echo "a != 3"
fi
字符 | 说明 |
---|---|
= | 相等 |
!= | 不等 |
-z “string” | 字符串长度为0 |
-n “string” | 字符串长度不为0 |
\< , < | 小于,[ ]会转义,所有要加\,[[ ]]不会转义 |
\> , > | 大于,同上 |
s1=aaa
s2=bbb
if [ $s1 \&amp;amp;amp;amp;amp;amp;lt; $s2 ]
echo "$s1 < $2"
fi
if [[ $s1 < $s2 ]]
echo "$s1 < $2"
fi
前面三个用的比较多,后面的用的很少。
字符 | 说明 |
---|---|
-e 文件名 | 文件存在 |
-f 文件名 | 文件存在且为普通文件 |
-d 文件名 | 文件存在且为目录 |
-r(w)(x) 文件名 | 文件存在且可读(写)(执行) |
-c(b) 文件名 | 文件存在且为字符型(块)特殊文件 |
-nt | 文件新于 |
-ot | 文件旧与 |
其他
[ ] | [[ ]] | 说明 |
---|---|---|
! | ! | 非 |
-a | && | 与 |
-o | || | 或 |
\( …\) | (…) | 括号,改变执行顺序 |
不支持 | =(or ==) | Patter matching模式匹配 |
不支持 | =~ | 正则表达式 |
case语句相当于绝大多数语言的switch语句,它除了具备if-elif的功能外,还支持通配符,这个相当有用。
#!/bin/bash
#提示用户输入一个选择,存入变量choice中,如果choice值与下面某个选项匹配则执行相应的命令
read -p "please input a choice:(yes|no?)" choice
case $choice in
[yY][eE][sS]|[yY])
echo yes
;;
[nN][oO]|[nN])
echo no
;;
*)
echo error
;;
esac
for e in item1 item2 ...
do
COMMANDS
done
示例:遍历当前目录所有文件
for file in `ls dir`
do
echo $file
done
示例:遍历一个给定的集合
list="a b c d e f g"
for e in $list
do
echo $e
done
for in
格式遍历集合时,是根据空白字符来分隔字符串的。
for ((i = 0; i < n; i++))
do
COMMANDS
done
这种for循环与C语言基本一致,适合于确定次数的循环。
#示例
for ((i = 0; i < 10; i++))
do
echo $i
done
#死循环
for (( ; ; ))
do
echo "abc"
done
while condition
do
COMMANDs
done
#死循环
while true
do
COMMANDS
done
condition与if语句中的一致。
与大多数语言一样,shell也支持这两种跳出循环的操作符。
function func_name()
{
COMMANDS
return RES
}
function关键字和()都可以省略,返回值也可以省略,如果不加返回,将以最后一条命令的结果作为返回值。函数参数与之前的位置参数相同,$1
表示第一个传入的参数。
func_name arg1 arg2
求子串
str=abcde
echo ${str:2:3}
#输出bcd,表达式中2代表偏移量,3代表长度。
求字符串长度
str=123456
echo ${#str}
#输出6
字符串替换
#将变量中第一个匹配替换为xx
${变量/pattern/xx}
#将变量中所有匹配替换为xx
${变量//pattern/xx}
删除子串
#从string的开头, 删除最短匹配substring的子串
${string#substring}
#从string的开头, 删除最长匹配substring的子串
${string##substring}
#从string的结尾, 删除最短匹配substring的子串
${string%substring}
#从string的结尾, 删除最长匹配substring的子串
${string%%substring}
在linux里,我们敲打的命令的终端其实就是一个shell,而shell里面会定义很多环境变量,如HOME
,PATH
等。然后我们在终端执行写好的bash脚本时,可以通过./script.sh
和source script.sh
两种方式执行。
第一种方式,会fork出一个新的子shell,然后执行脚本中的命令,在子shell中,父级export的变量还是可见的,比如所有的环境变量,但是父级的普通变量就不可见了,而子shell中的不管export的变量还是普通变量对父shell都是不可见的。
第二种方式,会在当前shell中执行脚本中的命令,因此脚本中的定义的变量就和在当前shell定义的变量时一样,所有要通过脚本来设置变量,必须通过source来执行脚本。
示例:test.sh
#!/bin/bash
SON_VAR="123"
echo $EXPORT_VAR
echo $NORMAL_VAR
shell环境执行以下命令:
# export EXPORT_VAR="abc"
# NORMAL_VAR="abc"
# ./test.sh
abc
# echo $SON_VAR
# source test.sh
abc
abc
# echo $SON_VAR
123