字符串

字符串替换

  • ${变量名#匹配规则} 从变量开头进行规则匹配,将符合最短的数据删除。
  • ${变量名##匹配规则} 从变量开头进行规则匹配,将符合最长的数据删除。
  • ${变量名%匹配规则} 从变量尾部进行规则匹配,将符合最短的数据删除。
  • ${变量名%%匹配规则} 从变量尾部进行规则匹配,将符合最长的数据删除。
  • ${变量名/旧字符串/新字符串} 变量内容符合旧字符串,则第一个旧字符串会被新字符串取代。
  • ${变量名//旧字符串/新字符串} 变量内容符合旧字符串,则全部旧字符串会被新字符串取代。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var_1="I love you, Do you love me"

    var=${var_1#*ov} # e you, Do you love me

    var=${var_1##*ov} # e me

    var=${var_1%ov*} # I love you, Do you l

    var=${var_1%%ov*} # I l

    var_2="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"

    # 第一个小写bin被替换为大写的BIN
    var=${var_2/bin/BIN} # /usr/local/BIN:/usr/bin:/bin:/usr/sbin:/sbin

    # 所有小写bin会被替换为大写的BIN
    var=${var_2//bin/BIN} # /usr/local/BIN:/usr/BIN:/BIN:/usr/sBIN:/sBIN

    抽取子串

  • ${string:position} 从string的 position 开始。
  • ${string:position:length} 从position开始,匹配长度为 length
  • ${string: -position} 从右边开始匹配。
  • ${string:(position)} 从左边开始匹配。
  • expr substr $string $postion $length 从 position 开始,匹配长度为 length

 

字符串长度

1
2
3
4
5
6
7
8
9

- ${#string}
- expr length "$string" 如果string有空格必须加双引号

var_1="Hello world"

len=${#var_1} # 11

len=`expr length "$var_1"` # 11

获取子串在字符串中的索引位置

  • expr index $string $substring 从1开始计算索引位置

    1
    2
    3
    4
    5
    var_1="quickstart is an app"

    ind=`expr index "$var_1" start` # 6
    ind=`expr index "$var_1" uniq` # 1
    ind=`expr index "$var_1" f` # 0

    计算子串长度

  • expr match $string substr

从头开始匹配子串长度,如果没有匹配到则返回0,匹配到了返回匹配子串的长度

1
2
3
4
5
var_1="quickstart is an app"

sub_len=`expr match "$var_1" app` # 0
sub_len=`expr match "$var_1" quic` # 4
sub_len=`expr match "$var_1" quic.*` # 18

字符串替换大小写

1
2
3
4
5
6
7
8
9
10
11
12
#! /bin/bash
# 注意:脚本第一行一定要注明脚本解释器是bash.不能是sh,或dash
# 因为sh软连接有可能指向的是dash
var="Hello,Word"
# 把变量中的第一个字符换成大写
echo ${var^}
# 把变量中的所有小写字母,全部替换为大写
echo ${var^^}
# 把变量中的第一个字符换成小写
echo ${var,}
# 把变量中的所有大写字母,全部替换为小写
echo ${var,,}

数组

定义数组

1
array=('v1' 'v2' 'v3') 

输出数组内容

1
2
3
${array[@]} # 输出全部内容
${array[*]} # 也是输出全部内容
${array[1]} # 输出下标索引为1的内容

获取数组长度

1
2
${#array} # 数组内元素个数
${#array[2]} # 数组内下标为2的元素长度

数组赋值

1
2
array[0]="frank" # 给数组下标为1的元素赋值为frank
array[20]="lion" # 在数组尾部添加一个新元素

删除数组

1
2
unset array[2] # 清除元素
unset array # 清空整个数组

数组遍历

1
2
3
for v in ${array[@]}
do
done

函数

定义

  • 函数名后面的圆括号不加任何参数

  • 函数的完整定义必须置于函数的调用之前

    1
    2
    3
    函数名 (){
    函数体
    }

    传参

    1
    2
    3
    4
    5
    6
    7
    #!/bin/bash

    print_something(){
    echo "hello $1" # $1 获取第一个参数
    }
    print_something Lion # Lion 为参数
    print_something Frank # Frank 为参数

    参数获取

    1
    2
    3
    4
    5
    6
    $1~$9:函数的第一个到第9个的参数。
    $0:函数所在的脚本名。
    $#:函数的参数总数。
    $@:函数的全部参数,参数之间使用空格分隔。
    $*:函数的全部参数,参数之间使用变量$IFS值的第一个字符分隔,默认为空格,但是可以自定义。
    $?:显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。可以用于函数返回值

    返回值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    testFun(){
    echo "helloworld!"
    return 99
    }


    # 千万要注意shell并不像其他语言直接返回返回值,其返回值放到$?中,这也是为什么只能返回整型的原因
    # 所以这种承接方法是错误的,获取到的值是echo打印的内容
    # return_value=`testFun`
    # 以下才是正确获取通过return返回的返回值的正确写法
    testFun
    echo "the return value is: $?"

    局部变量

  • 不做特殊声明,shell中变量都是全局变量

  • 局部变量 使用 local 关键字,函数内外同时存在同名变量,则函数内部会覆盖函数外部变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #!/bin/bash

    localvar="fun1"

    main() {
    func1
    func2
    }

    func1() {
    local localvar="funlocal"
    echo ${localvar}
    localvar="fun2"
    }

    func2() {
    echo ${localvar}
    }

    main "$@"

    循环

    until

    1
    2
    3
    4
    until [ 条件判断式 ]
    do
    程序
    done

    while循环

    1
    2
    3
    4
    while [ 条件判断式 ]
    do
    程序
    done

    for循环

    两种程式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //方式一
    for 变量 in123 …(可以是一个文件等)
    do
    程序
    done

    这种语法中for循环的次数,取决于in后面值的个数(空格分隔),有几个值就循环几次,并且每次循环都把值赋予变量。
    也就是说,假设in后面有三个值,for会循环三次,第一次循环会把值1赋予变量,第二次循环会把值2赋予变量,以此类推。


    //方式二
    for (( 初始值;循环控制条件;变量变化 ))
    do
    程序
    done

    语法二中需要注意:
    初始值:在循环开始时,需要给某个变量赋予初始值,如i=1;

    循环控制条件:用于指定变量循环的次数,如i<=100,则只要i的值小于等于100,循环就会继续;

    变量变化:每次循环之后,变量该如何变化,如i=i+1。代表每次循环之后,变量i的值都加1

逻辑控制

多分支case

每个程序之后;; 双分号结尾;以 case开头以esac结尾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@localhost ~]$ vi sh/if-case.sh
#!/bin/bash
read -p "请输入一个字符,并按Enter确认:" KEY

case "$KEY" in
[a-z]|[A-Z])
echo "您输入的是字母"
;;

[0-9])
echo "您输入的是数字"
;;

*)
echo "您输入的是其他字符"
;;
esac

if

1
2
3
4
5
6
7
8
9
10
if [ 条件判断式1 ]
then
当条件判断式1成立时,执行程序1
elif [ 条件判断式2 ]
then
当条件判断式2成立时,执行程序2
…省略更多条件…
else
当所有条件都不成立时,最后执行此程序
fi

文件

文件与目录

操作符 说明
pwd 查看当前目录
cd 切换目录 cd.. cd ~ cd /
ls 查看当前目录的文件列表
mkdir 创建目录或文件
echo 打印
cat 链接、查看文件 > , >>
less 文件内容过多,以分页方式查看
q 退出
mv 移动, . 代表当前目录;移动文件到一个目录,最后一个为目标目录
cp 复制文件或目录
rm 移除文件或者目录
rmdir 删除目录
1
2
3
4
5
6
7
8
9
10
11
12
# 移动文件到某个目录
mv combined.txt dir1

# . 代表当前目录
mv dir1/combined.txt .

# 移动多个文件到一个目录,最后一个为目标目录,如把以下几个文件及文件夹dir3移动到dir2
mv combined.txt test_* dir3 dir2

# 修改文件名、目录名
mv test_1.txt test1.txt
mv "folder 1" folder_1

运算

bash的运算有以下几种方式

$(( ))

只能计算整数

expr

语法格式: expr $num1 operator $num2

  • num1 | num2 – num1 不为空且非0,返回 num1 ; 否则返回 num2  
  • num1 & num2 – num1 不为空且非0,返回 num1 ;否则返回0
  • num1 < num2 – num1 小于 num2 ,返回1;否则返回0
  • num1 <= num2 – num1 小于等于 num2 ,返回1;否则返回0
  • num1 = num2 – num1 等于 num2 ,返回1;否则返回0
  • num1 != num2 – num1 不等于 num2 ,返回1;否则返回0
  • num1 > num2  – num1 大于 num2 ,返回1;否则返回0
  • num1 >= num2  – num1  大于等于 num2 ,返回1;否则返回0
  • num1 + num2 – 求和
  • num1 - num2 – 求差
  • num1 * num2 – 求积
  • num1 / num2 – 求商
  • num1 % num2 – 求余

    let

    let命令声明变量时 可以直接执行算术表达式
    1
    2
    let "foo = 1 + 2"
    echo $foo # 3

    运算符

    文件测试运算符

    文件测试运算符用于检测Unix/Linux文件的各种属性
操作符 说明
-b file 检测文件是否是块设备文件
-c file 检测文件是否是字符设备文件
-d file 检测文件是否是目录
-f file 检测文件是否是普通文件,不是目录或者设备文件
-g file 检测文件是否设置了SGID位
-p file 检测文件是否是明管道
-r file 检测文件是否可读
-w file 检测文件是否可写
-x file 检测文件是否可执行
-s file 检测文件是否为空
-e file 检测文件(包括目录)是否存在
  • -e $file exist 的缩写,表示文件是否存在。

  • -d $file directory 的缩写,表示文件是否为一个目录。

  • -f $file file 的缩写,表示文件是否是一个文件。

  • -L $file Link 的缩写,表示链接。

  • -r $file readable 的缩写,表示文件是否可读。

  • -w $file writable 的缩写,表示文件是否可写。

  • -x $file executable 的缩写,表示文件是否可执行。

  • $file1 -nt $file2 表示文件 file1 是否比 file2 更新。

  • $file1 -ot $file2 表示文件 file1 是否比 file2 更旧。

    字符串测试运算符

    运算符 说明
    = 检测两个字符串是否相等
    != 检测两个字符串是否不相等
    -z 检测字符串长度是否为0
    -n 检测字符串长度是否不为0
    str 检测字符串是否不为空
  • $string1 = $string2 表示两个字符串是否相等。

  • $string1 != $string2 表示两个字符串是否不相等。

  • -z $string 表示字符串 string 是否为空。

  • -n $string 表示字符串 string 是否不为空。

    数字测试运算符

    运算符 单词 说明
    -eq equal 检查两个数是否相等,相等返回true
    -ne not equal 检查两个数是否相等,不相等返回true
    -gt great than 检测左边的数是否大于右边的,如果是 返回true
    -lt less than 检测左边数是否小于右边数,如果是 返回true
    -ge great than or equal 检测左边数是否大于等于右边的,如果是,返回true
    -le less than or equal 检测左边数是否小于等于右边,如果是,返回true

如果在shell脚本中使用linux命令,可以使用$() 包裹命令,如 disk_size=$(df -h | awk ‘NR==2 {print $5}’)

  • $num1 -eq $num2 equal 的缩写,表示两个数字是否相等。

  • $num1 -ne $num2 not equal 的缩写,表示两个数字是否不相等。

  • $num1 -lt $num2 lower than 的缩写,表示 num1 是否小于 num2 。

  • $num1 -le $num2 lower or equal 的缩写,表示 num1 是否小于或等于 num2 。

  • $num1 -gt $num2 greater than 的缩写,表示 num1 是否大于 num2 。

  • $num1 -ge $num2 greate or equal 的缩写,表示 num1 是否大于或等于 num2

    逻辑判断

  • && 表示逻辑与,只要有一个不为真,整个条件测试为假。

  • || 表示逻辑或,只要有一个为真,整个条件测试就为真。

  • ! 表示反转测试条件。

    变量

    预定义变量

    预定义变量 作用
    $? 最后一次执行命令的返回状态,为0表示,上一个命令正确执行,否则代表上一个命令执行错误
    $$ 当前进程的进程号 PID
    $! 后台运行的最后一个进程号PID
1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost sh]$ ls
count.sh hello.sh parameter2.sh parameter.sh
#ls命令正确执行
[root@localhost sh]$ echo $?
#预定义变量“$?”的值是0,证明上一个命令执行正确
[root@localhost sh]$ ls install.log
ls:无法访问install.log:没有那个文件或目录
#当前目录中没有install.log文件,所以ls命令报错了
[root@localhost sh]$ echo $?
2
#变量“$?”返回一个非О的值,证明上一个命令没有正确执行
#至于错误的返回值到底是多少,是在编写ls命令时定义好的,如果碰到文件不存在就返回数值2

位置参数

位置参数变量
$n $0 表示当前脚本程序的名称,$1-9代表第一到第九参数,10以上参数需要大括号 {10}
$* 代表命令行中所有参数,$把所有参数看成一个整体
$@ 代表命令行中所有参数,每个参数区分对待
$# 代表命令行中所有参数的个数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@localhost sh]$ vi parameter2.sh
#!/bin/bash
for i in"$*"
#定义for循环,in后面有几个值,for会循环多少次,注意“$*”要用双引号括起来
#每次循环会把in后面的值赋予变量i
#Shell把$*中的所有参数看成是一个整体,所以这个for循环只会循环一次
do
echo "The parameters is: $i"
#打印变量$i的值
done
x=1
#定义变量x的值为1
for y in"$@"
#同样in后面的有几个值,for循环几次,每次都把值赋予变量y
#可是Shel1中把“$@”中的每个参数都看成是独立的,所以“$@”中有几个参数,就会循环几次
do
echo "The parameter$x is: $y"
#输出变量y的值
x=$(( $x +1 ))
#然变量x每次循环都加1,为了输出时看的更清楚
done

环境变量设置

export 声明的变量即是环境变量

环境变量查询和删除

env命令和set命令区别:set命令可以查看所有变量,而env命令只能查看环境变量

1
2
[root@localhost ~]$ unset gender   #删除环境变量gender
[root@localhost ~]$ env | grep gender

变量删除

unset 变量名

变量查看

set
-u 调用未声明变量会报错
-x 执行之前会把命令先输出一次
+<参数> 取消魔偶个set曾启动的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@localhost ~]$ set [选项]
选项:
-u:如果设定此选项,调用未声明变量时会报错(默认无任何提示)
-x:如果设定此选项,在命令执行之前,会把命令先输出一次
+<参数> :取消某个set曾启动的参数。

[root@localhost ~]$ set
BASH=/bin/bash
…省略部分输出…
name='shen chao'
#直接使用set 命令,会查询系统中所有的变量,包含用户自定义变量和环境变量
[root@localhost ~]$ set -u
[root@localhost ~]$ echo $file
-bash: file: unbound variable
#当设置了-u选项后,如果调用没有设定的变量会有报错。默认是没有任何输出的。
[root@localhost ~]$ set -x
[root@localhost ~]$ ls
+ls --color=auto
anaconda-ks.cfginstall.loginstall.log.syslog sh tdir testtestfile
#如果设定了-x选项,会在每个命令执行之前,先把命令输出一次

[root@localhost ~]$ set +x
#取消启动的x参数

细节

反引号

如果命令不用反引号包含,命令不会执行,而是直接输出
只有用反引号包含的命令、使用${命令}的方式也是可以

1
2
3
4
5
6
7
8
9
[root@localhost ~]$ echo ls
ls
#如果命令不用反引号包含,命令不会执行,而是直接输出
[root@localhost ~]$ echo `ls`
anaconda-ks.cfginstall.loginstall.log.syslog sh test testfile
#只有用反引号包括命令,这个命令才会执行
[root@localhost ~]$ echo $(date)
20181021日星期一18:25:09 CST
#使用$(命令)的方式也是可以的

单引号和双引号

如果输出使用单引号,则$name原封不动输出
如果输出使用双引号,则输出变量name的值sc
反引号和双引号括起来的命令会正常执行; 反引号被单引号括起来命令不会执行当做普通字符串输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@localhost ~]$ name=sc
#定义变量name 的值是sc(就是最正直的人,超哥我了!)
[root@localhost ~]$ echo '$name'
$name
#如果输出时使用单引号,则$name原封不动的输出
[root@localhost ~]$ echo "$name"
sc
#如果输出时使用双引号,则会输出变量name的值 sc

[root@localhost ~]$ echo `date`
20181021日星期一18:16:33 CST
#反引号括起来的命令会正常执行
[root@localhost ~]$ echo '`date`'
`date`
#但是如果反引号命令被单引号括起来,那么这个命令不会执行,―date`会被当成普通字符输出
[root@localhost ~]$ echo "`date`"
20181021日星期一18:14:21 CST
#如果是双引号括起来,那么这个命令又会正常执行

Shell反引号、$()和${}的区别

反引号与$()

用于命令替换,获取命令的返回值

1
2
all_files=`ls` # 获取ls命令的执行结果
all_files=$(ls) # 效果同上
  • ${}

用于变量替换

1
2
echo ${A}B

特殊变量替换

1
2
3
4
1. ${var:n} 若n为正数,n从0开始,表示在变量var中提取第n个字符到末尾的所有字符
2. ${var:n1:n2} 用于提取从下标n1开始后面n2个字符
3. ${/}与${//}用于字符串模式匹配替换
4. ${#}、${##}、${%}与${%%} 用于字符串模式匹配截断

[]和[[ ]] 、(())

1
2
3
在使用[]或者test指令进行字符串判空时,需要在引用的变量上加上双引号""。
如果使用[[]]的话就不需要。
$(())用来做整数运算的

参考

  1. shell学习教程
  2. 文件与目录
  3. Shell 函数
  4. Shell 空格
  5. shell脚本中各种括号的区别以及用法
  6. shell字符串大小写转换
  7. 阮一峰bash教程
  8. shell