一、Shell的输入输出
1.1 输出 -- echo
#!/bin/bash
#echo 的作用是将后边的内容打印到终端 自带换行
echo hello world
V1=nihao
echo ${V1}beijing
# -n 选项 表示不带换行符
V2="hello world"
echo -n $V2
# -e 选项 表示识别字符串中的转义字符 如换行符 \n
V3="nihao\nbeijing"
echo -e $V3
结果:
hello world
nihaobeijing
hello worldnihao
beijing
1.2 输入 -- read
阻塞等待在终端获取一个字符串 赋值给变量V1
#!/bin/bash
#阻塞等待在终端获取一个字符 赋值给变量V1
read V1
echo $V1
结果:
hello world ni hao
hello world ni hao
也可以一次输入多个字符串 输入时以空格分隔
如果一次read多个时 输入的内容本身包含空格 这种方式会有问题
不推荐一次输入多行
read V2 V3
echo V1=$V2
echo V2=$V3
结果:
hello world ni hao # 有空格就表示赋了一个值,剩下的赋给最后一个
V1=hello # 想给V1赋值hello world
V2=world ni hao # 因为有空格就默认剩下的赋值给下一个参数了
一般使用下面的用法,单个read 以回车分隔
#!/bin/bash
read V1
read V2
echo V1=$V1
echo V2=$V2
结果:
hello world
ni hao
V1=hello world
V2=ni hao
-p "string" 表示给输入添加提示信息
#!/bin/bash
read -p "输入一个字符串 >> " STRING
echo STRING=$STRING
结果:
输入一个字符串 >> hello world
STRING=hello world
-a 表示输入一个数组 输入时以空格分隔
#!/bin/bash
read -a ARR
echo ${ARR[@]}
echo ${#ARR[*]}
结果:
aa bb cc dd
aa bb cc dd
4
-n 4 表示最多读取4个字符 如果输入够4个字符 即使不敲回车 也会继续往下执行
#!/bin/bash
read -n 4 VALUE
echo $VALUE
结果:
sacesace
-t 5 表示最多等待5秒 5秒没有输入就继续执行 表示赋值为空
#!/bin/bash
read -t 5 V1
echo $V1
-s 表示取消输入的回显
#!/bin/bash
read -p "请输入信息: " -s V1
echo $V1
结果:
请输入信息: 1234567
#!/bin/bash
read -p "请输入你要下载的软件:" -n 20 -t 10 V1
sudo apt-get install $V1
$V1
二、Shell中的运算
Shell本身是用来执行命令的,不是用来做复杂运算的,如需要复杂运算,可以在Shell脚本里调用C程序完成
但是,一些场景中用到简单运算,例如计数变量自增,用C语言太麻烦,就可以用Shell编程算数运算
Shell如果不做特殊处理,所有运算符都会将变量的值当作字符串来处理
Shell不支持浮点数运算
shell中做算数运算的方式有4中:
(())
$[]
let
expr
上述四种运算方式,执行的效率是依次递减的。
一定要保证自己会用一种 推荐掌握 (()) 其他的作为了解
2.1 (())
((表达式))
((表达式1,表达式2,表达式3,... ,表达式n))
每个表达式都会参与运算,最后一个表达式的结果,就是整个(())运算符的结果
注意:
1.(())内部引用变量的值的时候 $ 可加可不加
2.(())内部运算符前后的空格 可加可不加
3.如果想引用整个(())表达式的结果 需要在前面加 $ -->$((表达式))
4.(())可以做复杂的算术运算:如:++ -- for循环等
for((i=0;i<5;i++))
do
done
#!/bin/bash
V1=10
V2=20
echo $((V1+V2))
echo $(($V1+$V2))
echo $(($V1 + $V2))
echo $((V1 + V2))
echo "------------------------------------------"
V3=100
((V3++)) #这种自增自减时 变量前 不可以加$
echo $V3
echo "------------------------------------------"
V4=10
V5=20
V6=30
RET=$((++V4,++V5,++V6))
echo $RET $V4 $V5 $V6
echo "------------------------------------------"
#循环执行5次ls
for((i=0;i<5;i++))
do
echo ni hao
done
echo "------------------------------------------"
结果:
30
30
30
30
------------------------------------------
101
------------------------------------------
31 11 21 31
------------------------------------------
ni hao
ni hao
ni hao
ni hao
ni hao
------------------------------------------
三、Shell中的if...else语句
if [ 表达式1 ] #if 和 [] 之间,[] 和表达式之间必须要有空格
then
代码块1
elif [ 表达式2 ] #if 和 [] 之间,[] 和表达式之间必须要有空格
then
代码块2
else #else分支没有then
其他分支
fi
3.1 对字符串的判断
注意:判断字符串时,最好用双引号把字符串引起来,否则,一旦字符串中有空格,就会报语法错误
-z 判断字符串是否为空
-n 判断字符串是否非空
==或= 判断字符串是否相等
!= 判断字符串是否不等
\> 判断字符串是否 前面大于后面
\< 判断字符串是否 前面小于后面
拓展:
> 和 >> 是输出重定向符
< 和 << 是输入重定向符
> 覆盖写
< 追加写
例:
#!/bin/bash
read -p "input str1 >>" str1
read -p "input str2 >>" str2
if [ -z "$str1" ]
then
echo "str1空"
else
echo "str1非空"
fi
echo "----------------------------------------"
if [ -n "$str2" ]
then
echo "str2非空"
fi
echo "----------------------------------------"
#判断字符串是否相等 用==或=都可以
if [ "str1" == "str2" ]
then
echo "=="
else
if [ "str1" \> "str2" ]
then
echo "str1 > str2"
else
echo "str1 < str2"
fi
fi
echo "----------------------------------------"
结果:
input str1 >>hello
input str2 >>hallo
str1非空
----------------------------------------
str2非空
----------------------------------------
str1 < str2
----------------------------------------
3.2 将字符串按整数的方式比较
-gt 大于 great then
-lt 小于 less then
-eq 等于 equal
-ge 大于等于 great equal
-le 小于等于 less equal
-ne 不等于 not equal
逻辑运算符
-a 逻辑与 && and
-o 逻辑或 || or
! 逻辑非 !
关于逻辑运算符:
在两个[]之间要用 &&
在一个[]内部要用 -a
#!/bin/bash
read -p "input V1 >>" V1
read -p "input V2 >>" V2
if [ "$V1" -eq "$V2" ]
then
echo "V1==V2"
else
if [ "$V1" -gt "$V2" ]
then
echo "$V1 > $V2"
else
echo "$V1 < $V2"
fi
fi
echo "--------------------------------"
#if [ "$v1" -gt 0 && "$v2" -gt 0 ] #错误
#if [ "$v1" -gt 0 ] -a [ "$v2" -gt 0 ] #错误
#if [ "$v1" -gt 0 ] && [ "$v2" -gt 0 ] #正确
if [ "$V1" -gt 0 -a "$V2" -gt 0 ]
then
echo "yes"
else
echo "no"
fi
结果:
input V1 >>32
input V2 >>23
32 > 23
--------------------------------
yes
#!/bin/bash
read -p "请输入你的成绩 >> " S
if [ "$S" -ge 90 -a "$S" -le 100 ]
then
echo "A"
elif [ "$S" -ge 80 ] && [ "$S" -lt 90 ]
then
echo "B"
elif [ "$S" -ge 70 -a "$S" -lt 80 ]
then
echo "C"
elif [ "$S" -ge 60 -a "$S" -lt 70 ]
then
echo "D"
else
echo "不及格"
fi
结果:
请输入你的成绩 >> 100
A
3.3 判断文件的类型
-e 判断文件是否存在 存在为真 不存在为假
-s 判断文件是否存在 且判断文件是否为非空 非空真 空假
-b 判断文件是否存在 且判断文件是否为块设备文件
-S(大写) 判断文件是否存在 且判断文件是否为套接字文件
-p 判断文件是否存在 且判断文件是否为管道文件
-f 判断文件是否存在 且判断文件是否为普通文件
-L(大写) 判断文件是否存在 且判断文件是否为符号链接文件
-c 判断文件是否存在 且判断文件是否为字符设备文件
-d 判断文件是否存在 且判断文件是否为目录文件
注意事项:
1.虽然 -s -b 等都具备判断文件是否存在的能力
但是我们使用时一般都是先用 -e 判断是否存在 存在的情况下再判断类型
否则 判断结果为假时 没法区分是类型不匹配导致的假 还是不存在导致的假
2.在判断链接文件时,他既满足链接文件的类型 也满足被链接文件的类型
所以一般判断类型时 我们都现判断链接文件
#!/bin/bash
read -p "请输入一个文件 >> " FILE
if [ -e $FILE ]
then
echo "文件存在"
if [ -s $FILE ]
then
echo "非空"
else
echo "空"
fi
else
echo "文件不存在"
fi
结果:
请输入一个文件 >> a.c
文件存在
非空
#!/bin/bash
read -p "请输入一个文件名 >> " FILE
if [ -e $FILE ]
then
if [ -L $FILE ]
then
echo "链接文件"
elif [ -f $FILE ]
then
echo "普通文件"
elif [ -d $FILE ]
then
echo "目录文件"
else
echo "其他文件"
fi
else
echo "文件不存在"
fi
结果:
请输入一个文件名 >> a.c
普通文件
3.4 判断文件权限
-r 判断文件是否存在 且判断文件是否有读权限
-w 判断文件是否存在 且判断文件是否有写权限
-x 判断文件是否存在 且判断文件是否有执行权限
注意:谁来执行脚本,判断的就是文件对谁的权限
#!/bin/bash
read -p "input a file >> " FILE
if [ -e $FILE ]
then
if [ -r $FILE ]
then
echo "有读权限"
else
echo "没有读权限"
fi
else
echo "文件不存在"
fi
结果:
input a file >> a.c
有读权限
3.5 判断文件的时间戳
文件的时间戳:文件最后一次被修改的时间
-nt (new than) 判断两个文件的时间戳 前面的是否比后面的新
-ot (old than) 判断两个文件的时间戳 前面的是否比后面的旧
注意:距离现在越近的越新
#!/bin/bash
read -p "input file >> " FILE1
read -p "input file >> " FILE2
if [ $FILE1 -nt $FILE2 ]
then
echo "1new"
else
echo "2new"
fi
结果:
input file >> 04
input file >> 02
2new
3.6 判断文件的inode号是否相同
只有同一组的硬链接文件 inode 号才相同
-ef 相同为真 不同为假
#!/bin/bash
read -p "input file >> " FILE1
read -p "input file >> " FILE2
if [ $FILE1 -ef $FILE2 ]
then
echo "相同"
else
echo "不同"
fi
结果:
input file >> a.c
input file >> link
相同
#!/bin/bash
read -p "input a file >> " FILE
if [ -e $FILE ]
then
if [ -f $FILE ]
then
if [ -w $FILE ]
then
echo "hello world" >> $FILE
tail -n 5 $FILE
else
echo "没有写权限"
fi
else
echo "不是普通文件"
fi
else
echo "文件不存在"
fi
结果:
input a file >> a.c
hello
hello
hello
hello world
hello world
四、shell中的 case..in 语句
4.1 格式
case $变量 in
选项1)
分支1
;;
选项2)
分支2
;;
...
*)
其他分支
;;
esac
注意:
1. case后面一定写的是变量的值 ($变量)
2.每个选项中的字符串就是case后面变量所有可能的结果
3. *选项 表示其他分支 相当于 switch..case 语句的 default分支 如果不需要 也可以不写
4.2 关于case..in语句的选项
"hello" 匹配hello
"aa"|"bb"|"cc" 匹配 aa 或者 bb 或者 cc
[0-9] 匹配0-9中的任意一个字符
[a-z] 匹配a-z中的任意一个字符
[abc] 匹配abc中的任意一个字符
#!/bin/bash
read -p "input a string >> " STR
case "$STR" in
"hello")
echo "is hello"
;;
"aa"|"bb"|"cc")
echo "is aa bb cc "
;;
hqyj[xmn])
echo "hqyj "
;;
file[abc][0-9])
echo "file"
;;
*)
echo "other"
;;
esac
结果:
input a string >> hello
is hello
思考:
如何能实现不管用户如何输入 y 后者 yes 都认为确认,否则是取消
y Y yes YES YeS yEs YEs ......
#!/bin/bash
read -p "您要删除吗? [Y/n] " C
case $C in
[Yy][Ee][Ss]|[Yy])
echo "yes"
;;
*)
echo "No"
;;
esac
结果:
您要删除吗? [Y/n] y
yes
五、shell中的 while 循环
5.1 格式
while [ 表达式 ]
do
循环体
done
其中 表达式的用法 和 if..else 语句的表达式用法一模一样
#!/bin/bash
i=1
while [ $i -le 100 ]
do
sum=$((sum+i))
((i++))
done
echo $sum #5050
结果:
5050
5.2 死循环
while [ 1 ]
do
循环体
done
while ((1))
do
循环体
done
while true
do
循环体
done
例:使用while循环每秒打印一次系统时间
#!/bin/bash
while ((1))
do
date
sleep 1
done
结果:
2024年 09月 14日 星期六 20:21:50 CST
2024年 09月 14日 星期六 20:21:51 CST
2024年 09月 14日 星期六 20:21:52 CST
^C
六、shell中的for循环
6.1 C风格的for循环
for ((i=0;i<5;i++))
do
echo $i
done
6.2 shell中特有的for循环
for 变量 in 单词列表
do
循环体
done
单词列表:
1.单词列表使用 空格 分割的,执行时 会自动将单词列表中的单词 逐个的赋值给变量,每赋值一次 循环执行一次
2.如果单词列表是连续的 可以使用 {start..end} 如 {1..100},也可以使用序列的写法 `seq 1 10`
如果有间隔 也可以使用 `seq 1 2 10` 表示 从1 到10 跨度为2 取值
3.单词列表也可以是命令的结果 (如 $(ls) 或者 `ls` )
4.省略in和单词列表的for循环,这种情况下 单词列表来自于执行脚本时命令行传的参数
#!/bin/bash
for i in aa "bb" 'hello'
do
echo hqyj-$i
done
echo "------------------------------"
for i in {1..10}
do
echo "hello world $i"
done
echo "------------------------------"
for i in `seq 1 2 10`
do
echo "hqyj $i"
done
echo "------------------------------"
结果:
hqyj-aa
hqyj-bb
hqyj-hello
------------------------------
hello world 1
hello world 2
hello world 3
hello world 4
hello world 5
hello world 6
hello world 7
hello world 8
hello world 9
hello world 10
------------------------------
hqyj 1
hqyj 3
hqyj 5
hqyj 7
hqyj 9
------------------------------
将当前目录下的所有普通文件权限修改为 0660
#!/bin/bash
for i in `ls`
do
if [ -f $i ]
then
chmod 0660 $i
fi
done
结果:
-rw-rw---- 1 linux linux 273 9月 14 20:30 28for.sh
-rw-rw---- 1 linux linux 77 9月 14 20:34 29for.sh
-rw-rw---- 1 linux linux 66 9月 14 20:05 a.c
lrwxrwxrwx 1 linux linux 3 9月 14 14:05 link -> a.c
#省略in和单词列表的for循环
#!/bin/bash
for i
do
echo $i
done
结果:
hello
world
七、shell中的break和continue
break 结束本层的循环
continue 结束本层的本次循环
break n n是一个数字 表示结束n层的循环
continue n n是一个数字 表示结束n层的本次循环
#!/bin/bash
for((i=1;i<=5;i++))
do
for((j=1;j<=5;j++))
do
if [ $j -eq 3 ]
then
#如果记不清 把此处注释打开 看现象对比就明白了
#break
#continue
#break 2
continue 2
fi
echo $i--$j
done
done
结果:
1--1
1--2
2--1
2--2
3--1
3--2
4--1
4--2
5--1
5--2
八、shell中select..in语句
select 变量 in 单词列表
do
语句
done
功能:将单词列表转换成一个可供用户选择的列表,方便用户操作
用户选择时 只能写编号 不能写单词。
#!/bin/bash
select i in beijing shanghai tianjin
do
echo 我选择的城市是$i
done
结果:
其中 #? 是提示符 可以通过修改环境变量 PS3 来自己指定
export PS3="input>>"
一般 select..in 语句会配合着 case..in 语句使用,供用户选择:
#!/bin/bash
select i in beijing shanghai tianjin
do
case $i in
beijing)
echo "人多"
;;
shanghai)
echo "钱多"
;;
tianjin)
echo "大爷多"
;;
esac
break
done
九、shell中的函数
#function可以不写 但是为了方便阅读代码 建议写上
function 函数名(){
函数体
}
注意:
1.shell中的函数是没有参数列表
2.shell中函数没有返回值类型
3.shell中的函数使用关键字function声明,function也可以不写,但是()必须写
4.shell中的函数名也要符合命名规范
5.shell中函数如果不被调用 也不会执行
9.2 函数的调用
没有参数: 函数名
有参数: 函数名 参数1 参数2 ... 参数n (空格分隔)
9.3 函数内部使用参数的方式
函数内部是通过 位置变量 来使用参数的
$0 脚本名
$1~$9 调用函数时传递的第1到第9个参数 超过10个时 使用 {} 如 ${123}
如果函数中想使用命令行的参数,需要 调用函数时,通过参数传递给函数
如果想将命令行的所有参数都传递给函数 要注意下面的用法
函数名 $@
函数名 $*
函数名 "$@"
注意:上面三种传参方式 是将命令行的参数 原封不动的逐个传递给函数
而下面的用法是将命令行的参数作为一个整体 传递给函数
函数名 "$*"
#!/bin/bash
function hqyj_test1(){
for((i=0;i<5;i++))
do
echo "hello world $i"
done
}
hqyj_test1 #没有参数的函数调用
echo "----------------------------------------"
#加法
function hqyj_add(){
echo $(($1+$2))
}
hqyj_add 10 20 #有参数的函数调用1
结果:
hello world 0
hello world 1
hello world 2
hello world 3
hello world 4
----------------------------------------
30
# 把指定路径下的所有文件的 其他人的写权限 加上
#!/bin/bash
function hqyj_chmod(){
for i in `ls $1`
do
chmod o+w "$1/$i"
done
echo "--------------------"
ls -l $1
echo "--------------------"
}
hqyj_chmod /home/linux/stage2/day4 #函数调用
结果:
-rw-rw-rw- 1 linux linux 273 9月 14 20:59 33function.sh
-rw-rw-rw- 1 linux linux 207 9月 14 21:07 34funchmod.sh
-rw-rw--w- 1 linux linux 66 9月 14 20:05 a.c
lrwxrwxrwx 1 linux linux 3 9月 14 14:05 link -> a.c
如果函数中想使用命令行的参数 需要 调用函数时 通过参数传递给函数
#!/bin/bash
function hqyj_test2()
{
echo $1
echo $2
echo $3
echo $4
}
hqyj_test2 hello world $1 $2
echo "--------------------------------"
function hqyj_test3()
{
echo $1
echo $2
echo $3
}
#hqyj_test3 $@ #逐个传参
#hqyj_test3 $* #逐个传参
#hqyj_test3 "$@" #逐个传参
hqyj_test3 "$*" #整体传参
结果:
hao
hello
world
hello
world
--------------------------------
hello world ni hao
9.4 函数的返回值
1.shell中的变量如果不加关键字 local 修饰 默认都是全局的
函数结束后 依然可以访问 所以可以使用全局变量来保存函数执行的结果
2.shell中也可以使用 return 来返回函数执行的结果
这种写法在调用处 需要使用 $? 来获取结果
这种写法只能返回 [0-255] 范围的值 超过这个值 就不能用了
3.可以使用echo来返回函数执行的结果
这种写法函数调用处需要使用命令置换符 $() 或者 `` 来接收
这种写法 函数中就只能有一句echo了 也就是说函数中就不能将信息打印到终端了
如果有多句echo 那函数的返回值也是多个echo的结果 (空格分隔)
#!/bin/bash
function hqyj_add1()
{
RET1=$(($1+$2))
}
hqyj_add1 10 20
echo $RET1
echo "------------------------"
function hqyj_add2()
{
return $(($1+$2))
}
hqyj_add2 20 30
echo $?
hqyj_add2 100 200
echo $? #100+200大于255
echo "------------------------"
function hqyj_add3(){
echo $(($1+$2))
#echo "hello" #如果写了 也会作为函数的返回值 不会按想象中的结果打印到终端
}
RET2=`hqyj_add3 10 20`
echo $RET2
RET3=$(hqyj_add3 1000 2000)
echo $RET3
结果:
30
------------------------
50
44
------------------------
30
3000