Linux下命令行程序叫做 Shell,除解释用户的输入命令,还可以:
-
调用其他程序,给其他程序传递数据或参数,并获取程序的处理结果;
-
在多个程序之间传递数据,把一个程序的输出作为另一个程序的输入;
-
Shell 本身也可以被其他程序调用。
Shell是一种脚本语言(常见的解释器有bash、sh、csh等),支持基本的编程元素,如:
-
if...else 选择结构,case 开关语句,for、while、until 循环;
-
变量、数组、字符串、注释、加减乘除、逻辑运算等概念;
-
函数,包括用户自定义的函数和内置函数(例如 printf、export、eval 等)。
Shell脚本也可以包含外部脚本(其他脚本文件),将外部脚本的内容合并到当前脚本。
. filename
#或
source filename
-
两种方式的效果相同,简单起见,一般使用点号(.),但是注意点号(.)和文件名中间有一空格。
-
被包含脚本不需要有执行权限。
printf函数
除用echo输出外,printf有更强的控制方式:
-
与c中的printf类似,但是参数不用括号,参数间用空格风格
-
参数比控制字符多时,也会按顺序输出;
-
参数比控制字符少时,%s用NULL代替,%d用0代替;
如:
printf '%s %d' a #输出:a 0
printf 'Test: %s %s' a b c d e #输出:
Test: a b
Test: c d
Test: e
变量基础
创建shell脚本时,必须在文件第一行指定要使用的shell:
#!/bin/bash
set -e # 遇到执行错误,退出(默认继续执行下一条)
set -u # (set -o nounset),使用未初始话变量时,脚本自动退出
脚本创建完成后,就需要执行脚本:
-
chmod u+x添加X权限,调用文件执行;如./file.sh
-
通过过sh file.sh来执行:不需要添加执行权限;
-
root用户可通过点号(.)来执行(不需执行权限),如:
. /lib/init/vars.sh
变量:
-
直接赋值即定义:count=100 #等号两边不能有空格
-
通过$来引用变量:$count或${count}
-
readonly修饰只读变量:readonly count=100
-
unset删除变量:unset count
如:
count=1
count=`expr $count+1`
特殊变量:
通过特殊变量可获取shell的参数:
-
$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。但是当它们被双引号(" ")包含时:
-
"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;
-
"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。
-
-
shift:移动变量,将每个参数变量减一;如$3变为$2;
-
if [ -n "$1" ]:来判断参数是否已传递。
-
更多的参数,需要加花括号引用:${10};
-
获取最后一个参数:${!#}。
变量替换:根据变量的状态(是否为空、是否定义等)来改变它的值
三种类型变量:
-
局部变量:在脚本或命令中定义,仅在当前shell实例中有效。
-
环境变量:所有的程序,包括shell启动的程序,都能访问环境变量。shell脚本也可以定义环境变量。
-
shell变量:shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行。
字符串类型
字符串可用单双引号:
-
单引号的限制:
-
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
-
单引号字串中不能出现单引号(对单引号使用转义符后也不行)。
-
-
双引号的优点:
-
双引号里可以有变量
-
双引号里可以出现转义字符
-
字符串连接:
-
直接联接两个字符串
-
newfile=${file}"new"
-
newfile=${file}${new}
-
-
使用printf(使用反引号)可以进行更复杂的联接
-
newstr=`printf "%s%s" "$STR" "$USER"`
-
字符串操作
表达式 |
含义 |
${#str} |
$str的长度 |
${str:position} |
在$str中,从位置$position开始提取子串 |
${str:position:length} |
在$str中,从位置$position开始提取长度为$length的子串 Position为0-n时表示从右边开始: l${str:0-7}:取末尾7个字符 l${str:0-7:3} |
${str#substring} |
从变量$str的开头,删除最短匹配$substring的子串 |
${str##substring} |
从变量$str的开头,删除最长匹配$substring的子串 |
${str%substring} |
从变量$str的结尾,删除最短匹配$substring的子串 |
${str%%substring} |
从变量$str的结尾,删除最长匹配$substring的子串 |
${str/old/new} |
使用$new,来代替第一个匹配的$old |
${str//old/new} |
使用$new,代替所有匹配的$old |
${str/#old/new} |
替换开头.如果$str以$old开头,那么就用$new替换 |
${str/%old/new} |
替换结尾.如果$str以$old结尾,那么就用$new替换 |
${VALUE%.*}:删除VALUE字符串中以分隔符“.”匹配的右边字符,保留左边字符。
${VALUE#*.}:删除VALUE字符串中以分隔符“.”匹配的左边字符,保留右边字符。
补充:
-
“*”表示通配符,用于匹配字符串将被删除的字串。
-
“.”表示字符串中分隔符,可以为任意一个或多个字符。
-
“%”表示从左向右匹配,“#”表示从右向左匹配,“/”表示替换,都属于非贪婪匹配,即匹配符合通配符的最短结果。而“%%”、“##”和“//”,都属于贪婪匹配,即匹配符合通配符的最长结果。
数组类型
数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组)。初始化时不需要定义数组大小,数组元素的下标由0开始。数组用括号来表示,元素用"空格"符号分割开,语法格式如下:
array_name=(value1 ... valueN)
定义:
myAry=(A B "CD" 1 2)
也可以使用下标来定义数组:
myAry[0]=value0
myAry[1]=value1
...
myAry[N]=valueN
读取:
${myAry[index]}
echo ${myAry} 输出第一个元素
使用@ 或 * 可以获取数组中的所有元素
echo "数组的元素为: ${myAry[*]}"
echo "数组的元素为: ${myAry[@]}"
长度:
获取数组长度的方法与获取字符串长度的方法相同
echo "数组长度: ${#myAry[*]}"
echo "数组元素个数: ${#myAry[@]}"
删除操作
清除某个元素:unset myAry[1],这里清除下标为1的数组;
清空整个数组:unset myAry;
切片访问
${array[@]:起始位置:长度}
-
中间以":"隔开,
-
起始位置省略从0开始;
-
长度省略,就取后面所有的项;
-
-
切片后返回的是字符串,可以通过 新数组=(${旧数组[@]:索引:长度})来获取
-
起始位置可以为负数,但必须以放在()中,长度不能为负数
${myAry[@]::4} #获取前四个元素
${myAry[@]:(-2)} #获取后两个元素
newAry=${myAry[@]:1:4} #生成新的切片
模式替换
${数组名[@或*]/模式/新值}
例如:${myAry[@]/2/98}
数组遍历
数组遍历我们使用for语句来演示:
for v in ${myAry[@]}; do
echo $v;
done
运算符说明
算术运算符:原生bash不支持简单的数学运算,但可以通过其他命令(如 awk 和 expr)来实现 :
-
+、-、*、/、%
-
=、==、!=
expr是一款表达式计算工具,使用它可以完成表达式的求值操作:
-
在 expr中的 表达式与运算符之间要有空格,否则错误;
-
在[ $a == $b ]与[ $a != $b ]中,要需要在方括号与变量以及变量与运算符之间也需要有空格, 否则错误。
如:
echo `expr $a + $b`
echo `expr $a \* $b` #乘号要转义
val=`expr 10 - 3` #表达式需要用反引号(`,~键那个)括起来
关系运算符:只支持数字(或值为数字的字符串)
-
-eq:两个数是否相等,相等返回 true。
-
-ne:两个数是否相等,不相等返回 true。
-
-gt:左边的数是否大于右边的。
-
-lt:左边的数是否小于右边的。
-
-ge:左边的数是否大等于右边的。
-
-le:左边的数是否小于等于右边的。
如:
[ 5 -eq 3 ] 返回 false
[ 5 -ne 3 ] 返回 true #方括号与变量以及变量与运算符之间也需要有空格
布尔运算符
-
!:非, [ !false ]为true
-
-o:或
-
-a:与, [ $a -a $b ],都为true是为true
字符串运算符
-
=:两个字符串是否相等,相等返回 true。
-
!=:两个字符串是否相等,不相等返回 true。
-
-z:字符串长度是否为0,为0返回 true。
-
-n:字符串长度是否为0,不为0返回 true。
-
[ str ]:字符串是否为空,不为空返回 true。
如,以a="test"为例
[ -z $a ] 返回 false
[ $a ] 返回 true
文件测试运算符:检测文件的各种属性
-
-b file:文件是否是块设备文件,如果是,则返回 true。
-
-c file:文件是否是字符设备文件,如果是,则返回 true。
-
-d file:文件是否是目录,如果是,则返回 true。
-
-f file:文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。
-
-r file:文件是否可读,如果是,则返回 true。
-
-w file:文件是否可写,如果是,则返回 true。
-
-x file:文件是否可执行,如果是,则返回 true。
-
-s file:文件是否为空(文件大小是否大于0),不为空返回 true。
-
-e file:文件(包括目录)是否存在,如果是,则返回 true。
-
-g file:文件是否设置了 SGID 位,如果是,则返回 true
-
-k file:文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。
-
-p file:文件是否是具名管道,如果是,则返回 true。
-
-u file:文件是否设置了 SUID 位,如果是,则返回 true。
如:
[ -r $file ] #是否可读
[ -e $file ] #是否存在
条件语句
shell语句默认使用回车分隔(每条语句一行),若要在一行中输入多条语句可通过分号分隔实现:
if [ *** ]; then
while [ *** ]; do
针对数字与字符串判断有扩展特性,放在判断条件时与if/while等间要保证有空格:
-
双括号(( expression )):用于数学表达式,内部可是任意的数学计算或比较表达式
if (( $var**2 > 90 ))
then
((var2=$var**2))
fi
-
双方括号[[ expression ]]:expression两边要加空格,用于字符串处理
while [[ "$a" > "$b" ]]
-
方括号[ expr ]:逻辑判断(见运算符说明部分)
if [ -d file ] 条件检测
if语句
if <Condition>
then
Command
else
Command
fi
case语句
case 值 in
模式1)
command...
;;
模式2)
command...
;;
*)
command...
;;
esac
-
取值后面必须为关键字 in,每一模式必须以右括号结束。
-
取值可以为变量或常数;匹配某一模式后,执行其后所有命令直至 ;;(双分号)。
-
如果无一模式匹配,执行*后的命令。
如:
for a in root xugd test none; do
echo $a
case $a in
root)
echo admin
;;
xugd | test ) #多个值可用|分隔
echo normal
;;
# *)
# echo default
# ;;
esac
done
for循环
for循环有两种常用用法,for...in与for((赋值;条件;运算语句))
for var in list # for((赋值;条件;运算语句))
do
commands
done
示例:循环十次
for((i=1;i<=10;i++));do
echo $(expr $i \* 3 + 1);
done
for i in $(seq 1 10)
do
echo $(expr $i \* 3 + 1);
done
#for i in {1..10};do
for i in 1 2 3 4 5;do #直接把值列出即可(用空格分隔)
echo $(expr $i \* 3 + 1);
done
示例:枚举文件
for file in /proc/*; do
echo $file
done
for file in $(ls *.sh); do
echo $file
done
while循环
while <condition>
do
action
done;
例:
i=10;
while [[ $i -gt 5 ]]; do
echo $i;
((i--));
done;
until循环
until <condition>
do
action
done
例:
a=10;
until [[ $a -lt 0 ]];do
echo $a;
((a—));
done;
# break 命令不执行当前循环体内break下面的语句从当前循环退出。
# continue 命令是程序在本循体内忽略下面的语句,从循环头开始执行。
shell函数
Shell函数必须先定义后使用,function关键字是可选的:
[function] function_name () {
list of commands
[ return value ]
}
注意:
-
调用函数只需要给出函数名,不需要加括号;
-
函数返回值,可以显式增加return语句;如果不加,会将最后一条命令运行结果作为返回值;
-
Shell 函数返回值只能是整数,一般用来表示函数执行成功与否,0表示成功,其他值表示失败;
-
函数的参数可以通过 $n 得到;
-
像删除变量一样,删除函数也可以使用 unset 命令,不过要加上 .f 选项。
Echo输出控制
通过echo可方便输出,并控制颜色格式:
-
-e 用来开启echo中的转义
-
设置颜色的格式:
-
echo -e "\033[字背景颜色;文字颜色m字符串"
-
echo -e "\e[0m":恢复默认
-
输出红色字体 abc,背景色不变;然后还原
echo -e '\033[0;31;1m abc \033[0m'
注:
-
文字颜色后面有个m
-
字符串前后可以没有空格,如果有的话,输出也是同样有空格
字颜色:30—–37
-
echo -e “\033[30m 黑色字 \033[0m”
-
echo -e “\033[31m 红色字 \033[0m”
-
echo -e “\033[32m 绿色字 \033[0m”
-
echo -e “\033[33m 黄色字 \033[0m”
-
echo -e “\033[34m 蓝色字 \033[0m”
-
echo -e “\033[35m 紫色字 \033[0m”
-
echo -e “\033[36m 天蓝字 \033[0m”
-
echo -e “\033[37m 白色字 \033[0m”
字背景颜色范围:40—–47
-
echo -e “\033[40;37m 黑底白字 \033[0m”
-
echo -e “\033[41;37m 红底白字 \033[0m”
-
echo -e “\033[42;37m 绿底白字 \033[0m”
-
echo -e “\033[43;37m 黄底白字 \033[0m”
-
echo -e “\033[44;37m 蓝底白字 \033[0m”
-
echo -e “\033[45;37m 紫底白字 \033[0m”
-
echo -e “\033[46;37m 天蓝底白字 \033[0m”
-
echo -e “\033[47;30m 白底黑字 \033[0m”
控制选项说明
-
\33[0m 关闭所有属性
-
\33[1m 设置高亮度
-
\33[4m 下划线
-
\33[5m 闪烁
-
\33[7m 反显
-
\33[8m 消隐
-
\33[30m — \33[37m 设置前景色
-
\33[40m — \33[47m 设置背景色
-
\33[nA 光标上移n行
-
\33[nB 光标下移n行
-
\33[nC 光标右移n行
-
\33[nD 光标左移n行
-
\33[y;xH设置光标位置
-
\33[2J 清屏
-
\33[K 清除从光标到行尾的内容
-
\33[s 保存光标位置
-
\33[u 恢复光标位置
-
\33[?25l 隐藏光标
-
\33[?25h 显示光标