Linux Shell编程参考大全
本文记录Linux Shell编程中常用基本知识,方便快速入门以及查询使用。
本文主要分为以下几个部分:
一、Shell中的变量
任何编程语言中,有关变量的定义,作用范围,赋值等都是最最基本的知识。
0、默认变量
首先介绍几个shell中的默认变量。
变量 | 含义 |
$0 | 当前脚本名称 |
$1 | 脚本接收的第一个参数 |
$2 | 脚本接收的第二个参数 |
$# | 脚本接收的所有参数个数 |
$@ | 脚本接收的所有参数 |
$* | 脚本接收的所有参数 |
$? | 前一行命令的执行状态 |
示例如下:
default_var.sh脚本内容
echo $0
echo $1
echo $2
echo $#
echo $@
echo $*
echo $?
1 hadoop@client:~$ sh default_var.sh a b c d
2
3 $0 ==> default_var.sh
4 $1 ==> a
5 $2 ==> b
6 $# ==> 4
7 $@ ==> a b c d
8 $* ==> a b c d
9 $? ==> 0
1、变量定义及赋值
hadoop@client:~$ v1=hello
hadoop@client:~$ echo $v1
hello
但是要注意的是,赋值处必须为一个整体,不能有空格。
1 hadoop@client:~$ v2=hello world
2 No command 'world' found,did you mean:
3 Command 'tworld' from package 'tworld' (universe)
4 world: command not found
想要包含空格,需要用单引号或者双引号包围,如:
1 hadoop@client:~$ a="hello"
2 hadoop@client:~$ b="$a world"
3 hadoop@client:~$ echo $b
4 hello world
5 hadoop@client:~$ c='$a world'
6 hadoop@client:~$ echo $c
7 $a world
可以看到,单引号中的$a保持原样输出。而双引号中的$a会替换成其变量值。
3、`符号
这个符号在数字键1的的左侧,与单引号很类似。但是其功能与单引号双引号都有不同。在该符号中的命令会被执行。
hadoop@client:~$ d=`date`
hadoop@client:~$ echo $d
Wed Dec 28 06:31:13 PST 2019
如果不想使用这个符号,可以用$()替换
1 hadoop@client:~$ e=$(date)
2 hadoop@client:~$ echo $e
3 Wed Dec 28 06:31:48 PST 2019
4、命令行交互read
有时候我们希望在脚本运行时能根据用户的输入决定脚本后续执行逻辑,比如在安装插件的时候经常会让用户选择输入[N/Y]的时候。
比如有一个脚本script_test.sh
read -p "Please input [Y/N]: " yn
if ["$yn" == "N" -o "$yn" == "n" ]; then
echo "NO"
elif ["$yn" == "Y" -o "$yn" == "y"]; then
echo "YES"
fi
在运行时根据用户的输入决定if分支走向。运行结果如下:
hadoop@client:~$ sh script_test.sh
Please input [Y/N]: y
YES
read命令的使用形式为:
read [-pt] variable
参数p: 后面可以接提示符
参数t: 后面可以接秒数
例如,
read -p "Please input name" -t 5 name
表示将输入内容赋值给变量name,用户有5秒钟的输入时间。
5、定义变量类型declare
默认情况下,变量的赋值内容都是字符类型的。例如以下代码,我们期待的是输出一个求和值,但是输出的是一个求和表达式。
hadoop@client:~$ sum=100+300+500
hadoop@client:~$ echo $sum
100+300+500
如果想要输出求和后的值,可以使用declare命令。
hadoop@client:~$ declare -i sum=100+300+500
hadoop@client:~$ echo $sum
900
declare命令的使用形式如下:
declare [-aixr] variable
参数a: 将variable定义为数组
参数i: 将variable定义为整型(integer)
参数x:将variable设置成环境变量,类似于export的作用
参数r: variable为readonly类型,值不能被更改
二、Shell中的集合类型
1、数组(array)
(1)数组定义和赋值
数组中的元素用括号包围,各元素之间空格隔开。例如
hadoop@client:~$ array_name=(v0 v1 v2 v3)
可以重新设置指定元素的内容,如下所示
hadoop@client:~$ array_name[2]=v22 hadoop@client:~$ echo ${array_name[2]}
v22
(2)数组元素访问
输出该数组中所有元素:
hadoop@client:~$ echo ${array_name[*]}
v0 v1 v22 v3
hadoop@client:~$ echo ${array_name[@]}
v0 v1 v22 v3
数组元素下标从0开始,想要访问指定位置的元素,使用[]指定下标值,如下所示
hadoop@client:~$ echo ${array_name[0]}
v0
hadoop@client:~$ echo ${array_name[1]}
v1
hadoop@client:~$ echo ${array_name[2]}
v2
hadoop@client:~$ echo ${array_name[3]}
v3
(3)获取数组长度
获取数组长度使用如下命令
hadoop@client:~$ echo ${#array_name[@]}
4
hadoop@client:~$ echo ${#array_name[*]}
4
获取数组中单个元素的长度如下命令
hadoop@client:~$ echo ${#array_name[2]}
3
2、map
map类型中存储的都是键值对。
在Shell中定义map变量如下所示:
declare -A m=(["a"]="1" ["b"]="2")
输出所有的key值
hadoop@client:~$ echo ${!m[@]}
a b
输出所有的value
hadoop@client:~$ echo ${m[@]}
1 2
输出指定key对应的value
hadoop@client:~$ echo ${m["a"]}
1
hadoop@client:~$ echo ${m["b"]}
2
添加元素
hadoop@client:~$ m["c"]=3
hadoop@client:~$ echo ${m["c"]}
3
map中键值对的个数
hadoop@client:~$ echo ${#m[@]}
三、Shell中的字符串操作
在任何语言中对字符串的操作都是非常频繁的。字符串的操作主要包括,字符串截取,字符串替换等。
接下来的示例中,都以字符串http://blog.csdn.net/dabokele作为初始字符串。
str="http://blog.csdn.net/dabokele"
1、字符串删除
删除前面的http://
hadoop@client:~$ echo ${str#http://}
blog.csdn.net/dabokele
#从前往后截取,%从后往前截取。
示例中表示将符合的最短数据删除,如果使用两个#,或者两个%,则表示将符合的最长数据删除。
如下所示,匹配最短时,会将https://csdn.ne删除,匹配最长时,会将全部字符删除。
hadoop@client:~$ echo ${str#http://b*e}
t/daboleke
hadoop@client:~$ echo ${str##http://b*e}
2、字符串截取
可以从字符串的指定位置开始截取,同时可以指定截取的位数,如下所示:
hadoop@client:~$ echo ${str:2} //从第二位开始截取到最末尾,第一个字符下标为0
tp://blog.csdn.net/dabokele
hadoop@client:~$ echo ${str:2:3} //从第二位开始顺序截取三个字符
tp:
hadoop@client:~$ echo ${str:(-6):3} //从倒数第六位开始,截取三个字符,最后一个字符
bok
3、字符串替换
将http替换成HTTP
hadoop@client:~$ echo ${str/http/HTTP}
HTTP://blog.csdn.net/dabokele
- 使用一个斜杠(/)表示只替换成第一个遇到的字符。
- 使用两个斜杠(//)则表示替换全部符合的字符。
- 使用#匹配以指定字符开头的字符串。
- 使用%匹配以指定字符串结尾的字符串。
hadoop@client:~$ echo ${str/e/E}
http://blog.csdn.nEt/dobokele
hadoop@client:~$ echo ${str//e/E}
http://blog.csdn.nEt/dobokele
hadoop@client:~$ echo ${str/#h/H} //匹配开头的那个h
Http://blog.csdn.net/dobokele
hadoop@client:~$ echo ${str/%e/E} //匹配最后那个e
http://blog.csdn.net/dobokelE
4、字符串默认值
假设以下这个场景,如果变量name没有赋过值,则给一个默认值default,否则使用指定的值。
hadoop@client:~$ echo $name
hadoop@client:~$ echo ${name-default}
default
hadoop@client:~$ name="ckm"
hadoop@client:~$ echo ${name-default}
ckm
但是,如果已经将变量name设置成“”,则结果如下:
hadoop@client:~$ name=""
hadoop@client:~$ echo ${name-default}
如果变量内容为“”或者变量未初始化则给默认值,可以在-前加个冒号,使用:-
hadoop@client:~$ name=""
hadoop@client:~$ echo ${name-default}
hadoop@client:~$ echo ${name:-default}
default
5、字符串拼接
字符串拼接如下所示
hadoop@client:~$ echo "aaa""bbb"
aaabbb
hadoop@client:~$ echo "aaa"$str
aaahttp://blog.csdn.net/dabokele
hadoop@client:~$ echo "aaa$str"
aaahttp://blog.csdn.net/dobokele
6、字符串长度
求字符串长度用#操作,如下所示
hadoop@client:~$ echo ${#str}
29
7、字符串split成数组
在以空格为分割字符串成数组时操作最简单。
hadoop@client:~$ s="a b c d e"
hadoop@client:~$ a=($s)
hadoop@client:~$ echo ${a[*]}
a b c d e
hadoop@client:~$ echo $a[2]
c
所以,如果需要指定特定字符进行分割,而原字符串中没有空格时,可以先将特定字符替换成空格,然后按照上述进行分割,如下所示,
hadoop@client:~$ s="a,b,c,d,e"
hadoop@client:~$ a=(${str//,/ })
hadoop@client:~$ echo ${a[*]}
a b c d e
hadoop@client:~$ echo ${a[2]}
c
如果字符串中本身已有空格,并且期望的分割符不是空格,按如下方法进行分割。首先将IFS变量替换成指定字符,分割后将IFS更新为原字符。
hadoop@client:~$ s="a b,c,d,e"
hadoop@client:~$ old_ifs="$IFS"
hadoop@client:~$ IFS=","
hadoop@client:~$ a=($s)
hadoop@client:~$ IFS="$old_ifs"
hadoop@client:~$ echo ${a[*]}
a b c d e
hadoop@client:~$ echo ${a[0]}
a b
hadoop@client:~$ echo ${#a[*]}
4
8、字符串包含
有时候需要判断字符串str1中是否包含字符串str2,使用=~操作符。
str1="hello"
str2="ell"
if [ [$str1 =~ $str2] ];then
echo "$str1 contains $str2"
fi
查看运行结果
hadoop@client:~$ sh script_test.sh
hello contains ell
四、Shell中的控制结构
0、循环接受脚本参数shift
测试脚本如下:
echo '原始参数: ' $*
shift
echo 'shift后参数: ' $*
shift 2
echo 'shift 2后参数: ' $*
查看脚本运行结果
hadoop@client:~$ sh script_test.sh a b c d e f g
原始参数: a b c d e f g
shift后参数: b c d e f g // 移除第一个参数a
shift 2后参数: d e f g // 继续移除前两个参数b和c
1、条件表达式
(1) if ... then
判断表达式是经常用到的。整体结构如下所示,其中if和fi是必须的,中间的elif和else是可选的。
if [ 判断条件1 ];then
执行内容1
elif [ 判断条件2 ];then
执行内容2
else
执行内容3
fi
a)算术运算符
算术运算符的使用格式如下
a=10
b=20
val=`expr $a + $b`
expr命令一般用于整数值,但也可用于字符串。一般格式为:
expr argument operator argument
expr也是一个手工命令行计数器。
expr 10 + 10
20
(注意运算符左右都有空格 ,如果没有空格表示是字符串连接)
使用乘号时,必须用反斜线屏蔽其特定含义。因为shell可能会误解显示星号的意义。
expr 30 \* 3
90
常用的算术运算符包括
运算符 | 说明 | 举例 |
+ | 加法 | expr $a + $b 结果为 30。 |
- | 减法 | expr $a - $b 结果为 10。 |
* | 乘法 | expr $a \* $b 结果为 200。 |
/ | 除法 | expr $b / $a 结果为 2。 |
% | 取余 | expr $b % $a 结果为 0。 |
= | 赋值 | a=$b 将把变量 b 的值赋给 a。 |
== | 相等 | 用于比较两个数字,相同则返回 true。[ $a == $b ] 返回 false。 |
!= | 不相等 | 用于比较两个数字,不相同则返回 true。[ $a != $b ] 返回 true。 |
b)关系运算符
关系运算符的使用格式如下:
a=10
b=20
$a -eq $b
常用的关系运算符包括
运算符 | 说明 | 举例 |
-eq | 检测两个数是否相等,相等返回true。 | [ $a -eq $b ] 返回true。 |
-ne | 检测两个数是否相等,不相等返回true。 | [ $a -ne $b ] 返回true。 |
-gt | 检测左边的数是否大于右边的,如果是,则返回true。 | [ $a -gt $b ] 返回false。 |
-lt | 检测左边的数是否小于右边的,如果是,则返回true。 | [ $a -lt $b ] 返回true。 |
-ge | 检测左侧的数是否大等于右边的,如果是,则返回true。 | [ $a -ge $b ] 返回false。 |
-le | 检测左边的数是否小等于右边的,如果是,则返回true。 | [ $a -le $b ] 返回true。 |
c)布尔运算符
常用的布尔运算符如下
运算符 | 说明 | 举例 |
! | 非运算,表达式为true则返回false,否则返回true。 | [ ! false ] 返回 true。 |
-o | 或运算,有一个表达式为true则返回true。 | [ $a -lt 20 -o $b -gt 100 ] 返回true。 |
-a | 与运算,两个表达式都为true才返回true。 | [ $a -lt 20 -a $b -gt 100 ] 返回false。 |
d)字符串运算符
常用的字符串运算符如下
运算符 | 说明 | 举例 |
= | 检测两个字符串是否相等,相等返回true。 | [ $a = $b ] 返回 false。 |
!= | 检测两个字符串是否相等,不相等返回true。 | [ $a != $b ]返回true。 |
-z | 检测字符串长度是否为0,为0返回true。 | [ -z $a ] 返回false。 |
-n | 检测字符串长度是否为0,不为0返回true。 | [ -n $a ] 返回true。 |
str | 检测字符串是否为空,不为空返回true。 | [ $a ] 返回 true。 |
e)文件测试运算符
常用的文件测试运算符如下
运算符 | 说明 | 举例 |
-b file | 检测文件是否是块设备文件,如果是,则返回true。 | [ -b $file ] 返回false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回true。 | [ -c $file ] 返回false。 |
-d file | 检测文件是否是目录,如果是,则返回true。 | [ -d $file ] 返回false。 |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回true。 | [-f $file ] 返回true。 |
-g file | 检测文件是否设置了SGID位,如果是,则返回true。 | [ -g $file] 返回false。 |
-k file | 检测文件是否设置了沾着位(Sticky Bit),如果是,则返回true。 | [ -k $file ] 返回false。 |
-p file | 检测文件是否具有管道,如果是,则返回true。 | [ -p $file ] 返回false。 |
-u file | 检测文件是否设置了SUID位,如果是,则返回true。 | [ -u $file ] 返回false。 |
-r file | 检测文件是否可读,如果是,则返回true。 | [ -r $file ] 返回true。 |
-w file | 检测文件是否可写,如果是,则返回true。 | [ -w $file ]返回true。 |
-x file | 检测文件是否可执行,如果是,则返回true。 | [ -x $file ] 返回true。 |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回true。 | [ -s $file ] 返回true。 |
-e file | 检测文件(包括目录)是否存在,如果是,则返回true。 | [ -e $file ] 返回true。 |
(2)case...esac
case表达式的使用格式如下
case $变量 in
"内容1")
程序1
;;
"内容2")
程序2
;;
*) #匹配其他所有情况
程序3
;;
esac
看一个示例,如果第一个参数为hello,则打印hello world。如果第一个参数是bye,则打印bye bye。如果是另外的情况,则输出该参数。
case $$1 in
"hello")
echo "hello world"
;;
"bye")
echo "bye bye"
;;
"*")
echo $1
;;
esac
运行结果如下
hadoop@client:~$ sh script_test.sh hello
hello world
hadoop@client:~$ sh script_test.sh bye
bye bye
hadoop@client:~$ sh script_test.sh hehe
hehe
2、循环表达式
(1)while do done,until do done
while循环的格式如下
while [ condition ]
do
程序
done
与while
循环相反的是until
循环。
until [ condition ]
do
程序
done
在while循环中,当条件满足时,就执行其中的程序。而条件不成立时就终止循环。
下面举例用两种循环来实现当输入为yes时跳出循环。
a) while循环示例如下
while [ "$yn" != "yes" ]
do
read -p "Please input yes to stop: " yn
done
echo "stop!"
运行结果
hadoop@client:~$ sh script_test.sh
Please input yes to stop: no
Please input yes to stop: no
Please input yes to stop: yes
Stop!
b) until循环示例如下
until [ "$yn" == "yes" ]
do
read -p "Please input yes to stop: " yn
done
echo "Stop!"
运行结果如下:
hadoop@client:~$ sh script_test.sh
Please input yes to stop: no
Please input yes to stop: yes
Stop!
(2)for ... do ... done
for循环的格式如下
for var in con1 con2 con3 ...
do
程序
done
下面这个示例循环打印输入参数列表
for arg in $*
do
echo $arg
done
运行结果如下
hadoop@client:~$ sh script_test.sh a b c d e
a
b
c
d
e
(3)for ... do ... done的另一种形式
for ((初始值;目标值;步长))
do
程序
done
循环输出1到10中的奇数
for((i=1;i<=10;i=i+2))
do
echo $i
done
运行结果如下
hadoop@client:~$ bash script_test.sh
1
3
5
7
9
五、Shell中的函数
在shell中也可以像其他编程语言那样,将代码封装成函数。Shell中的函数,需要注意以下两点:
1、函数定义
由于Shell是从上往下执行的,所以在定义函数之前就调用该函数的话,会提示command not found,例如:
echo "Before :" `printFunc`
function printFunc(){
echo "print function !"
}
echo "After :" `printFunc`
运行结果如下,前一个函数调用处提示command not found
hadoop@client:~$ sh script_test.sh
2_script_test.sh: line 3: printFunc: command not found
Before :
After : print function !
2、函数参数与shell参数
在Shell定义的函数,是可以传递和接受参数的。在子函数中,$1表示接受的第一个参数...,这里需要注意与shell参数的区别。
但是测试后发现$0表示的仍然是shell名称。如果想要显示当前函数名,可以使用$FUNCNAME参数,如下所示,
function printStr(){
echo "printStr function print: $FUNCNAME"
echo "printStr function print: $*"
}
echo "main print: $0"
echo "main print: $*"
printStr f1 f2 f3
运行结果如下:
[hadoop@client ~]$ sh script_test.sh m1 m2 m3
main print: script_test.sh
main print: m1 m2 m3
printStr function print: printStr
printStr function print: f1 f2 f3
六、vi快捷操作
下面操作中出现大写字母,比如G表示需要同时按住Shift和G键。n表示输入的数字。其他比如text则表示字符串。
1、跳转
按键 | 说明 |
gg | 跳转到第一行 |
G | 跳转到最后一行 |
ngg/nG | 跳转到最后一行 |
Ctrl+f | 向下翻页 |
Ctrl+b | 向上翻页 |
h | 光标左移 |
j | 光标下移 |
k | 光标上移 |
l | 光标右移 |
w | 移到下一个单词的开头 |
W | 移到下一个单词的开头,忽略标点 |
b | 移到上一个单词的开头 |
B | 移到上一个单词的开头,忽略标点 |
e | 移到下一个单词的末尾 |
E | 移到下个单词的末尾,忽略标点 |
nw/nW/nb/nB | 跳转n个单词 |
L | 移到当前屏幕最后一行 |
M | 移到当前屏幕中间行 |
$ | 到当前行最后一个字符 |
^ | 到当前行第一个字符 |
0 | 到当前行第一个字符 |
n | |
( | 到句子的开头 |
) | 到句子的结尾 |
{ | 到段落的开头 |
} | 到段落的结尾 |
2、查找和替换
(1)查找
按键 | 说明 |
/text | 向后查找text字符 |
?text | 向前查找text字符 |
n | 跳转至下一个text字符 |
N | 跳转至上一个text字符 |
:set ic | 查找时忽略大小写 |
:set noic | 查找时对大小写敏感 |
(2)替换
按键 | 说明 |
:s/oldtext/newtext/ | 替换当前行第一个oldtext为newtext |
:s/oldtext/newtext/g | 替换当前行所有oldtext为newtext |
:m,ns/oldtext/newtext/ | 在m行到n,用newtext替换第一个oldtext |
:1,$s/oldtext/newtext/ | 在1行到最后一行,用newtext替换第一个oldtext |
:m,ns/oldtext/newtext/g | 在m行到n,用newtext替换oldtext |
:1,$s/oldtext/newtext/g | 在1行到最后一行,用newtext替换oldtext |
在最后输入一个c(confirm),表示替换前弹出确认提示。按y则逐一替换当前光标处匹配的字符,n则跳出当前光标处字符,a替换全部匹配的字符。
3、复制、删除、撤销、重复
(1)复制
按键 | 说明 |
yy | 复制当前行 |
nyy | 复制光标所在的向下n行内容 |
p | 将复制内容黏贴到下一行 |
P | 将复制内容黏贴到上一行 |
另外,输入y+跳转中的操作,可以为复制指定方向。比如yw,从当前位置复制到下一个单词的开头。这样可以进行复制一个单词的操作。
(2)删除
按键 | 说明 |
dd | 删除当前行(按p可黏贴) |
ndd | 从当前行向下删除n行 |
dG | 从当前行删除最最后一行 |
dgg | 从当前行删除到第一行 |
:n,md | 从第n行删除到第m行 |
x | 删除当前字符 |
nx | 删除当前向后n个字符 |
X | 删除光标前的字符 |
nX | 删除当前向前n个字符 |
另外,输入d+跳转中的操作,可以为删除指定方向。比如dw,从当前位置删除至下一个单词开头。
(3)撤销
按键 | 说明 |
u | 撤销上一次操作 |
U | 撤销当前行所有操作 |
. | 重复最后一次操作 |
(4)重复
按键 | 说明 |
. | 重复最后一次操作 |
4、其他
按键 | 说明 |
J | 将下一行连接到本行末尾 |
nJ | 将下n行连接到本行末尾 |
~ | 将当前字符切换大小写 |
n~ | 将当前向后n个字符切换大小写 |
g~~ | 切换当前行大小写 |
u | 列编辑模式下,选中列转换成小写 |
U | 列编辑模式下,选中列转换成大写 |
guu | 当前行转换成小写 |
gUU | 当前行转换成大写 |
guw | 当前单词转换成小写 |
gUw | 当前单词转换成大写 |
5、列编辑
按Ctrl+V,进入列编辑模式
(1)删除列
进入列编辑模式,
移动光标,选中需要删除的列,
按d,则会删除选中内容。
(2)插入列
进入列编辑模式,
移动光标选中需要插入内容的列。
按shift+i,会在选中列的第一行输入想要插入的内容。
连续按两次ESC,则会在选中的列处全部插入输入字符。