【学习分享】shell脚本基础(全)
Shell
1、基础语法
1.1 变量
1.1.1 规则
与java、python的变量规则一致,大致包含以下规则:
-
只包含字母、数字和下划线
-
不能以数字开头
-
避免使用 Shell 关键字(和其他语言是一样的)
-
使用大写字母表示常量
-
避免使用特殊符号和空格
1.1.2 使用
在使用变量前,增加美元符号$
name='zhangsan'
有一个点是需要注意的,python和java当中变量的赋值,等号的左右两边会增加空格去进行区分,但是shell当中不需要,增加了甚至可能报错
echo $name
或者 echo ${name}
本质上两种方式都是一样的,可加可不加,加括号是为了帮助解释器识别变量的边界,但是建议是都加上
1.1.3 只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
这个概念有点像java当中的私有变量,可用 可读,但是无法改变
name="lisi"
readonly name
这样name这个变量就只读不可用,若强行改变,会报错
1.1.4 删除变量
使用 unset 命令可以删除变量
#!/bin/sh
name="zhangsan"
unset name
echo name
上述代码无任何输出结果
1.1.5 类型
-
字符串,使用方式和其他语言一样,也是可拼接的,但是有一个需要注意地方,单引号和双引号的区别
区别:单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;单引号字符串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用
双引号里可以有变量;双引号里可以出现转义字符
#!/bin/sh name=“zhangsan” test=“hello,${name}” echo test #输出 hello zhangsan test_1='hello,${name}' echo test_1 #输出 hello,${name}
#获取字符串的长度 s="test" echo $(#s) # 输出4 #第二种获取字符串长度的方式 echo $(#s[0]) # 输出4,本质上当变量为字符串时,${#string} 等价于 ${#string[0]} #查找子字符串 t="abcdefg" echo `expr index "$t" ab` #输出1,输出`ab`这个字符串第一次出现的位置
获取字符串长度
-
整数变量,和其他语言一样。有个特点是,多了两个关键字“ declare”“typeset ”,这样的声明告诉 Shell 将 my_integer 视为整数,如果尝试将非整数值赋给它,Shell会尝试将其转换为整数
declare -i age=18
-
数组变量(仅支持一维数组,不支持多维)
aray=(1 2 3 4) #定义数组的形式
#读取数组
s=$(aray[0]) #获取aray数组中的第一个值
h=$(aray[@]) # 获取aray数组中的所有值
###########
: '注释'
1.2、参数传递
我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为 $n,n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数。
例如可以使用 $1、$2 等来引用传递给脚本的参数,其中 $1 表示第一个参数,$2 表示第二个参数,依此类推
#!/bin/bashecho “$0” #输出脚本名称 包含路径echo "$1" #输出第一个参数 后续的参考上述概念
特殊字符释义:
-
$#:传递到脚本的参数个数(例如:传递的参数为 a b 从,调用该命令,输出3)
-
$*:把所有参数以字符串的形式都传递到脚本
-
$$:脚本运行的当前进程ID号
-
$!:后台运行的最后一个进程的ID号
-
$@:与$*有点相似,在最后具体说明差异
-
$-:显示shell当前选项
-
$?:显示最后命令的退出状态,0表示没有任何错误,其他值是有错误的
$* 与 $@ 区别:
-
相同点:都是引用所有参数。
-
不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,则 " * " 等价于 "1 2 3"(传递了一个参数),而 "@" 等价于 "1" "2" "3"(传递了三个参数)。
1.3 数组
1.3.1 数组基础概念
shell的数组命名规则和变一致,格式和主流的编程语言也是一致的,唯一的区别是,数组当中不同的值是用空格隔开的,而非逗号。
#!/bin/bash
array=(1 2 3 4 5 6)
echo $(array[0]) #shell的数组下标也是从0开始而非1,打印第一个数据“1”
1.3.2 关联数组
基础概念:可以使用任意的字符串、或者证书作为下标来访问数组元素。通俗一点的理解就是,普通的数组访问数据内部的元素只能使用下标,且下标只能是阿拉伯数字,关联数组就突破了这个限制,这个“下标”不再是数字,可以是字符串形式。
语法格式:declare -A array_name ,其中-A生命一个关联数组,且键唯一。
#!/bin/bash
declare -A array_test=(["a"]="test1" ["b"]="test2" ["c"]="test3")
echo "打印array_test的值"
echo ${array_test["A"]} #输出test1
echo ${array_test["B"]} #输出test2
echo ${array_test["C"]} #输出test3
echo ${array_test[*]} #输出test1、test2、test3
echo "打印结束"
#也可以先定义,再赋值
declare -A array_test1
array_test1["A"]="kkk"
array_test1["B"]="lll"
array_test1["C"]="JJJ"
echo "开始"
echo ${array_test1["A"]}
echo ${array_test1["B"]}
echo ${array_test1["C"]}
echo "结束"
1.3.3 数组键获取办法
再打印所有值的前面增加感叹号,见以下代码的打印行
#!/bin/bash
declare -A array_test=(["a"]="test1" ["b"]="test2" ["c"]="test3")
echo "打印array_test的值"
echo ${!array_test[*]}
echo "打印结束"
1.3.4 数组长度获取
再打印所有值的前面增加#,见以下代码的打印行
#!/bin/bash
declare -A array_test=(["a"]="test1" ["b"]="test2" ["c"]="test3")
echo "打印array_test的值"
echo ${#array_test[*]}
echo "打印结束"
1.4 运算符
1.4.1 算数运算符
和大多数编程语言一样,包含+ 、-、*、/、%、=、==、!= ,具体的可以自己了解。
#原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
#!/bin/bash
val=`expr 1 + 1`
echo "和为:${val}" #输出2
val=`expr 1 \* 1`
echo "积为:${val}" #输出1
val=`expr 1 - 1`
echo "差为:${val}"
val=`expr 8 / 2`
echo "商为:${val}"
val=`expr 8 % 3`
echo "余数为:${val}"
1.4.2 关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字
-
-eq:判断两个数是否相等,相等返回true
-
-ne:判断两个数是否不相等,不相等返回true
-
-gt:左边数字是否大于右边,若是,返回true
-
-lt:左边的数字是否小于右边,若是,返回true
-
-ge:左边的数字是否大于等于右边的,若是,返回true
-
-le:左边的数字是否小于等于右边的,若是,返回true
#!/bin/bash
#代码片来自菜鸟编程
a=10
b=20
if [ $a -eq $b ]
thenecho "$a -eq $b : a 等于 b"
elseecho "$a -eq $b: a 不等于 b"
fi
if [ $a -ne $b ]
thenecho "$a -ne $b: a 不等于 b"
elseecho "$a -ne $b : a 等于 b"
fi
if [ $a -gt $b ]
thenecho "$a -gt $b: a 大于 b"
elseecho "$a -gt $b: a 不大于 b"
fi
if [ $a -lt $b ]
thenecho "$a -lt $b: a 小于 b"
elseecho "$a -lt $b: a 不小于 b"
fi
if [ $a -ge $b ]
thenecho "$a -ge $b: a 大于或等于 b"
elseecho "$a -ge $b: a 小于 b"
fi
if [ $a -le $b ]
thenecho "$a -le $b: a 小于或等于 b"
elseecho "$a -le $b: a 大于 b"
fi
1.4.3 布尔运算符
与或非的运算逻辑与其他的编程语言一致
-
!:非运算
-
-o:或运算
-
-a:与运算
#!/bin/bash
#代码片来自菜鸟编程
a=10
b=20
if [ $a != $b ]
thenecho "$a != $b : a 不等于 b"
elseecho "$a == $b: a 等于 b"
fi
if [ $a -lt 100 -a $b -gt 15 ]
thenecho "$a 小于 100 且 $b 大于 15 : 返回 true"
elseecho "$a 小于 100 且 $b 大于 15 : 返回 false"
fi
if [ $a -lt 100 -o $b -gt 100 ]
thenecho "$a 小于 100 或 $b 大于 100 : 返回 true"
elseecho "$a 小于 100 或 $b 大于 100 : 返回 false"
fi
if [ $a -lt 5 -o $b -gt 100 ]
thenecho "$a 小于 5 或 $b 大于 100 : 返回 true"
elseecho "$a 小于 5 或 $b 大于 100 : 返回 false"
fi
1.4.4 逻辑运算符
和布尔有点像,只不过这个是数学中的逻辑符号
-
&&:逻辑的and,全真才为真,一假则假
-
||:逻辑或,一真则真,全假才为假
#!/bin/bash
#代码片来自菜鸟编程
a=10
b=20
if [[ $a -lt 100 && $b -gt 100 ]]
thenecho "返回 true"
elseecho "返回 false"
fi
if [[ $a -lt 100 || $b -gt 100 ]]
thenecho "返回 true"
elseecho "返回 false"
fi
1.4.5 字符串运算符
这些运算符多用于字符串的操作
-
=:两个字符串如果相等,返回真
-
!=:两个字符串如果不相等,返回假
-
-z:字符串长度为0,则返回真
-
-n:字符串长度不为0,返回真
-
$:字符串内容不为空,返回真
#!/bin/bash
# 代码片来自菜鸟教程
a="abc"
b="efg"
if [ $a = $b ]
thenecho "$a = $b : a 等于 b"
elseecho "$a = $b: a 不等于 b"
fi
if [ $a != $b ]
thenecho "$a != $b : a 不等于 b"
elseecho "$a != $b: a 等于 b"
fi
if [ -z $a ]
thenecho "-z $a : 字符串长度为 0"
elseecho "-z $a : 字符串长度不为 0"
fi
if [ -n "$a" ]
thenecho "-n $a : 字符串长度不为 0"
elseecho "-n $a : 字符串长度为 0"
fi
if [ $a ]
thenecho "$a : 字符串不为空"
elseecho "$a : 字符串为空"
fi
1.4.6 文件测试运算符
文件测试运算符用于检测Unix文件的各种属性
-
-b file:若文件是块设备文件,返回true
-
-c file:若文件是字符设备文件,返回true
-
-d file:若文件为目录,返回true
-
-f file:若文件是普通文件,返回true
-
-g file: 若文件设置了SGID位,返回true
-
-k file:若文件设置了sticky bit,返回true
-
-p file:若文件设置了有名管道,返回true
-
-u file:若文件设置了SUDI位,返回true
-
-f file:若文件可读,返回true
-
-w file:若文件可写,返回true
-
-x file:若文件可执行,返回true
-
-s file:若文件部不为空,返回true
-
-e file:若文件存在,返回true
1.4.7 自增/自减
和java等一些主流语言的自增和自减的逻辑是一样的,但是shell使用的是let命令
#!/bin/bash
num=1
let num++
let num++
let num--
echo ${num} # 输出1
1.5 命令
1.5.1 echo命令
该命令用过很多了,用于字符串输出,上述的例子用了很多
#!/bin/bash
echo "hello world" #输出hello word
#显示普通字符串,下述两种方式的输出一致
echo "hello world"
echo hello world
#显示转义字符
echo "\"hello world"\" # 输出 "hello world"
#显示变量
test=33
echo "他的年龄是:${test}" # 输出 他的年龄是:33
#显示换行
echo -e "OK \n" # -e 的作用是开启转义
#显示不换行
echo -e "OK \c" # -e 开启转义 \c 不换行
#显示结果定向至文件
echo "hello world" > file_name
#原样输出
echo "$name\" #输出“$name\”
#显示命令执行结果
echo `date`
1.5.2 printf命令
printf和大多数的编程语言一样,用于输出,使用该关键字会比echo的移植性更好一些,但是不会想echo自动添加换行符,需要手动添加\n
1.5.2.1 printf格式符:
格式说明符由%开始,后跟一个或者多个字符,用于制定输出的格式,常用的格式说明符:
-
%s:字符串
-
%d:十进制数
-
%f:浮点数
-
%c:字符
-
%x:十六进制数
-
%o:八进制数
-
%b:二进制数
-
%e:科学计数法表示浮点数
#!/bin/bash
printf "hello" #打印hello且不换行
printf "hello\n" #打印hello且换行
#printf的实际用法
printf "%-10s %-10s %-10s\n" 你好 我的名字是 张三 #%s是格式代替符号,后续的三个为给代替符号赋值,“-10”标识左边对齐,默认为右对齐,且任何字符都会被显示在10个字符宽的字符内,不足的话则空格补齐
1.5.2.2 printf转义字符
\a:警告字符
\b:后退
\c:抑制输出结果中任何结尾的换行符,旨在%b格式指示符控制下的参数字符串钟有效
\f:换页
\n:换行
\r:回车
\t:水平制表符
\v:垂直制表符
\:一个字面上的反斜杠字符
\ddd:表示1到3位数八进制的字符,仅在格式字符串中有效
1.6 流程控制
shell的流程控制里面无法为空
1.6.1 if else
if的逻辑和java和python1等主流的编程语言一致,只是书写不太相同
#!/bin/bash
#!/bin/bash
a=1
b=2
if (($a > $b))
thenecho "a大"
else echo "b大"
fi
if else -if else
#!/bin/bash
a=1
b=2
if (($a > $b))
thenecho "a 大"
elif (($a == $b))
thenecho "a b 相等"
elseecho "b大"
fi #放在末尾 结束循环
#上述的也可以写成一行,只不过可读性比较差,写成一行的话,需要用分号隔开
if [(($a > $b))];then echo "a大";elif [(($a == $b))];then echo "a b 相等";else echo"b大";fi
1.6.2 for循环
for循环的话和其他的编程语言是一致的,关键字和写法有一点区别
#!/bin/bash
for i in 1 2 3 4 5 6
doecho $i
done #输出 1 2 3 4 5 6
1.6.3 while循环
while循环和主流编程语言的循环是一致的,本身也是死循环,直到不满足循环条件,退出循环
#!/bin/bash
a=0
while (($a < 5))
doecho $alet a++
done 输出0 1 2 3 4
#while循环还可以无限循环 只要循环的条件永远为真
while true #就可以构成无限循环
1.6.4 until 循环
这个和while循环是反过来的,while是条件不再为真的时候停止,而until循环是条件不再为假的时候停止
#!/bin/bash
a=10
until (($a < 3))
doecho $alet a--
done
1.6.5 case esac语句
这个就是switch..case相似的,分支结构的语句
#!/bin/bash
#代码片来自菜鸟编程
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in1) echo '你选择了 1';;2) echo '你选择了 2';;3) echo '你选择了 3';;4) echo '你选择了 4';;*) echo '你没有输入 1 到 4 之间的数字';;
esac
1.6.6 总结
shell脚本的循环控制存在跳出指令,break和continue语句,两者的使用区别和使用与java python一致。
区别:break是跳出当前循环,且结束后续循环,continue是跳出当前的此轮循环,但是不停止下一次伦循环。
1.7 函数
函数的定义和javascrip有点类似,直接上例子
#!/bin/bashmyFun(){ #定义一个名称为myFun的函数a=0echo "hello world" #函数体return $a}echo "begin"myFun # 函数调用echo $? #打印函数返回值 也就是函数中的a的值echo "end"
函数的传参形式有点不一样,请看例子
#!bin/bash
myFun(){echo "参数1:$1" #打印参数1echo "参数2:$2" #打印参数2
}
myFun 10 11 #调用函数 并且输入参数1和2 分别为10 和11
1.8 shell输入/输出重定向
输入输出的重定向,比较简单的理解就是,改变脚本的输入和输出方向。正常来说,使用终端运行脚本,会将最终结果显示在终端,输出重定向后,输出会保存到指定的文件当中,输入重定向亦是如此。
命令 | 说明 |
---|---|
command > file | 将输出重定向到 file。 |
command < file | 将输入重定向到 file。 |
command >> file | 将输出以追加的方式重定向到 file。 |
n > file | 将文件描述符为 n 的文件重定向到 file。 |
n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
n >& m | 将输出文件 m 和 n 合并。 |
n <& m | 将输入文件 m 和 n 合并。 |
<< tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |
直接上代码
#!/bin/bash
#创建目标txt文件夹
echo "hello world"<log.txt #输出将打印在log.txt当中
echo "happy"<<log.txt #输出将接在hello world下面 若是"<" 则是覆盖
#同理,输入重定向
cat data.txt #读取该文档的内容
read a < data.txt #读取该文件数据
echon "获取的数据是:$a" #输出
一般情况下,每个 Linux 命令运行时都会打开三个文件:
-
标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
-
标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
-
标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
#!/bin/bash
fun 1 2 3 2>errorlog.txt #调用未定义的函数fun并赋值,程序会报错,且报错信息会谢若errorlog文件当中
echo "正常输出" 2>errorlog.txt #输出正常值,errorlog文件不会出现运行结果
#如果要将正常输出和非正常输出都放在一个文件,可以有下面的写法
myFun(){echo "正常输出" fun
}>log.txt 2>&1
myFun #输出内容为:“正常输出” 报错信息“未找到命令”