第二十一天(shell俗称“脚本”的初学)练习答案见下一章
shell : 就是一个命令解释器
将人类的语言翻译成计算机能看得懂的语言
将计算机的反馈翻译成人可以看得懂语言
俗称脚本
写shell脚本,后缀为 .sh
写shell的时候前面应该要指定用哪一个版本
我们直接使用vim打开一个新文件
如果这个文件是新文件,vim会自动给我们指定我们的shell版本
#!/bin/bash
#:在shell里面是注释的意思
shell里面所有的东西都是字符串
echo -> 输出接到控制台(终端)
echo "hello world"
shell里面的变量
shell里面的变量无需定义,直接使用
所有数据都是字符串
变量名=值
我们要使用这个变量的值需要用$
${变量名}
$变量名
eg:
bianliang=SB250
echo $bianliang
echo ${bianliang}
shell里面的变量分为四种
1 用户自定义变量 --- 自己使用的
直接使用即可
shell在使用可以使用终端上面所有的可以使用的命令
ls -l
直接写直接使用
有的时候我们希望得到这个命令的反馈
`命令` -> 我要引用这个命令的返回值
var=`date`
var=`ls -l`
2 位置变量
根据这个变量的位置来确定它的值
用于程序传参或者函数传参
$0 -> 第一个参数 ./1.sh
$1 $2 $3 ..... -> 真正的参数
$@ -> 包含所有的命令行参数,单个的字符串
$@ <-> "$1" "$2" .....
$* -> 包含所有的命令行参数,将所有的参数看成一个整体,是一个字符串
$* <-> "$1 $2 ....."
3 环境变量 -- 这个变量在同一个终端下面所有的进程都可以使用
我们运行这个程序,必须要找到这个程序才能运行
路径/程序名 -> 回车就运行这个程序
如果直接写程序名 回车会提示没有这个命令
但是我们写cd ls cp mv .....从来没有写过这些程序的路径
cd是一个文件,它必然放在某一个文件夹里面
而这个文件是所有的进程都知道的一个文件夹
那么我使用cd的时候,进程实际上已经知道它的路径了,所以就没有必要写这个路径了
-> 这个路径就是我们所谓的环境变量
环境变量就是用于保存这些路径的,只要在这个路径里面,所有的进程都会知道
那么我们就不需要写他们的路径了
环境变量的变量名 : PATH
echo $PATH
输出的是
/home/china/.vscode-server/bin/b3e4e68a0bc097f0ae7907b217c1119af9e03435/bin/remote-cli:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/arm/5.4.0/usr/bin:/snap/bin
不同的路径通过:隔开
cd cp就在上面的某一个路径里面保存着
我们运行自己的程序的时候也希望这样,不想写他们的路径
1 将我们的可执行程序文件放入PATH对应的那些文件夹里面某一个里面去
2 让可执行程序的文件夹也再这个PATH里面,不就可以了吗
都是加的绝对路径
export PATH=$PATH:/你的路径名
eg:
我现在的路径是/mnt/hgfs/E/实体班/HN2502/第一阶段/shell/code/
export PATH=$PATH:/mnt/hgfs/E/实体班/HN2502/第一阶段/shell/code/
HOME -> 保存我们的家目录
LD_LIBRARY_PATH -> 库的环境变量
4 特殊变量
$? -> 上一个命令的退出码
funcname
$? -> 获取funcname的返回值
$# -> 参数的个数
.....
shell数组
数组名[下标]=值 下标:0 1 2 n-1
array[0]=250
array[50]=sb
引用数组元素
${数组名[下标]} 引用单个元素
${数组名}/$数组名 /bin/bash里面是取第一个元素
你要引用整个数组也是可以的
${数组名[*]} 或者 ${数组名[@]}
shell功能性语句
任何合法的命令都可以使用,包括我们自己写的
read -> 默认从终端获取输入
read var1 var2 -> 从终端或者两个字符串,第一个放在var1,第二个放在var2
重定向 --- 本来有一个默认的,我给你换一个地方
1 输入重定向
将我们的输入来源指定到另外的一个文件或者设备
在命令后面指定一个 < filename
2 输出重定向
将我们的输出目标指定到另外的一个文件或者设备
echo默认终端
两种形式(根据不同的需求来进行选择,文件不存在会先创建)
1: > filename
先清空filename原有的内容,然后在输出到这个文件
2: >> filename
追加,直接往末尾进行写入
3.sh
练习:写一个shell
从一个文件里面获取一些内容,写到另外一个文件里面去
expr : 算术命令,用于简单的整数计算
由于这个玩意儿是命令 ,因此需要用``包起来,才能得到它的运算结果
+ - \* / %
eg:
expr 3 + 5
val=`expr 3 + 5`
4.sh
test : 测试,这个玩意儿可以测试三种对象
字符串 整数 文件
测试成功返回0
测试失败返回1
1 测试字符串
= 测试两个字符串是不是完全相同
eg:
test "abc" = "abc"
$? #得到上一条命令的返回值 这里就是test测试结果
!= 测试两个字符串是不是不相同
eg:
test "abc" != "abc"
$?
-z 测试字符串是不是为空
eg:
test -z "abc"
$? #测试结果为1
test -z ""
$? #测试结果为0(真)
-n 测试字符串是不是不为空,跟上面是反的
test -n "abc"
$? #测试结果为0(真)
test -n ""
$? #测试结果为1
问题:
val1="" #这个玩意儿是空的
val2="dsadaws"
#test "" = $val2#这里测试的$val1相当于什么都没有写
#这就缺了一个操作数 报错
#其中一个为空串很正常 因此我们需要解决这个问题
#加上一个字符/或者多个字符
#我们应该这么来 防止出现变量是空串的情况
test ${val1}x = ${val2}x
#或者
test "${val1}" = "${val2}"
echo ------$?
-z -n必须采用下面这种方式
test -z "${val1}"
2 整数测试
-eq : 测试两个整数是不是相等的 ==
-ne : 测试两个整数是不是不相等的 !=
-gt : 测试前面的是不是大于后面的 >
-ge : 测试前面的是不是大于等于后面的 >=
-lt : 测试前面的是不是小于后面的 <
-le : 测试前面的是不是小于等于后面的 <=
eg:
test $a -ne $b -> 测试a不等于b 不等于就是0 等于就是1
.....
3 文件测试
-d filename 测试filename是不是一个文件夹
-f filename 测试filename是不是一个普通文件
-L filename 测试filename是不是一个链接
-r filename 测试filename是不是存在并且可读
-w filename 测试filename是不是存在并且可写
-x filename 测试filename是不是存在并且可执行
-s filename 测试filename是不是存在并且长度不为0
filename1 -nt filename2 测试filename1是不是新于filename2(文件的修改时间)
newer than
filename1 -ot filename2 测试filename1是不是老于filename2
older than
test写的时候比较麻烦,因此搞了一个简写
test可以简写为 []
test exp <-> [ exp ]
test $a -ne $b <-> [ $a -ne $b ]
test内置操作符,连接两个条件
-a and 并且
-o or 或者
test $a -gt $b -a $a -gt $c #a > b && a > c
test $a -gt $b -o $a -gt $c #a > b || a > c
[ $a -gt $b -a $a -gt $c ]
[ $a -gt $b -o $a -gt $c ]
条件操作符(&& || !),用于创建复合表达式,和上面的有区别
test $a -gt $b && test $a -gt $c
<-> [ $a -gt $b ] && [ $a -gt $c ]
test $a -gt $b || test $a -gt $c
<-> [ $a -gt $b ] || [ $a -gt $c ]
! [ $a -gt $b ] -> 非
5.sh
shell结构性语句(分支,循环)
1 分支
if commod ; then
语句
else # 没有else也可以
fi
commod:可以是任意的合法语句,可以执行的shell命令....
eg:从键盘获取两个整数,输出较大的那个
read val1 val2
if [ $val1 -gt $val2 ] ; then
echo $val1
else
echo $val2
fi
6.sh
2 多路分支(像c的switch)
case 字符串变量 in
模式1)
语句1
.....
;;#这个一定要写 类似与c的break
模式2)
语句2
.....
;;#这个一定要写 类似与c的break
....
模式n)
语句n
.....
;;#这个一定要写 类似与c的break
esac#这里表示case结束了
这里面nb的就是模式,不是简单的一个个的匹配
模式可以进行通配
* : 任意多个字符
? : 任意单个字符
eg:
你有一个文件名,判断是一个什么样子的文件
.c .cpp .h
case filename in
*.c)
echo "${filename} 是一个c文件"
;;
*.cpp)
echo "${filename} 是一个c++文件"
;;
*.h)
echo "${filename} 是一个头文件"
;;
esac
练习:
1 从键盘输入一个整数,判断它是奇数还是偶数
read a
num=`expr a % 2`
if [ num -eq 0 ] ; then
echo "$a 是偶数"
else
echo "$a 是奇数"
fi
2 判断这个文件是不是一个.mp3/.mp4文件
上面改几个名字
3 循环语句
for 变量名 in 单词表
do
.......
done
单词表:以空白符分隔开的字符串列表
eg:
"a bc def ghijk"
变量的值为当前单次,每循环一次,自动往后面走一个单词
单词走完for就结束了
eg:将一个单词表的所有的单词进行输出
for val in a bc def ghijk "a bc def ghijk"
do
.......
done
while循环
while 命令或者表达式
do
done
until循环
until 命令或者表达式
do
done
while 和 until结构是一样的
不同until测试假才执行
while测试为真才执行
for循环在实际使用过程中,如果处理的是整数,我们可以写成c风格的
for ((i=1;i < 100;i = i + 1))
do
语句
done
练习:
1 从1 + 2 ..... + 100
2 判断一个整数是不是质数
3 计算一个文件有多少行
while read val
do
#read在循环里面重定向到一个文件
done < 1.txt
shell函数 在调用之前进行定义
function_name()
{
完成函数
.......
$1就是第一个参数
$2就是第二个参数
$3就是第三个参数
......
}
如果你需要参数,可以,直接传参就可以了
函数里面通过位置去拿参数
$1 $2 $3.......
调用函数:
function_name 参数1 参数2......
如果我们想得到他的返回值
r=`function_name 参数1 参数2......`
这个值是echo输出值
输出的是什么 r就得到什么值
函数返回用return
return 后面给一个值
return相当于是函数的退出,函数就相当于有一个退出码了
用$?
9.sh
循环的时候我们可以在某一个时候退出,或者继续下一次循环
break n
continue n
n表示第几层
break 2 表示跳出底层循环
continue 2 结束这一次循环,从第二层循环那里继续循环
break和continue后面不写值也是可以的,跳出/继续本层循环
执行shell的时候我们也可以指定在什么时候退出这个脚本
exit n
程序运行到这一句就会退出
n为退出码
命令可以后台执行
命令/运行什么东西 &
-> 这个命令就会转入后台执行
-> 这个东西经常会碰得到的
练习:
请你写一个shell脚本,计算你这段时间总共写了多少句代码
统计你的share共享文件夹里面所有的.c .h文件总共有多少行
如果你是按照我写的那个统计有多少行不行
有空白行,你需要过掉
找所有的.c .h文件
r=`find /mnt/hgfs/E/实体班/HN2502 -regex ".*/*\.[ch]"`
#循环r这个单词表就可以了 每一个单词都是一个文件
基础逻辑给你们写一个
10.sh
grep -E -c "\S" /mnt/hgfs/E/实体班/HN2502/第一阶段/正则表达式/code/regex.c
#########################################################################
# File Name: 22.sh
# Author: csgec
# mail: 12345678@qq.com
# Created Time: 2025年07月28日 星期一 15时56分27秒
#########################################################################
#!/bin/bash#1 从1 + 2 ..... + 100
#法1
a=0
sum=0
until [ $a -gt 100 ]
dosum=`expr $sum + $a`a=`expr $a + 1`
done
echo $sum
#法2
b=0
sum1=0
while [ $b -le 100 ]
do sum1=`expr $sum1 + $b`b=`expr $b + 1`
done
echo $sum1
#法3
sum2=0
for ((i=0;i<=100;i++))
dosum2=`expr $sum2 + $i`
done
echo $sum2#2 判断一个整数是不是质数
read num
for ((i=2;i<num;i++))
doif [ `expr $num % $i` -eq 0 ]thenecho "不是质数"exitfi
done
echo "是质数"#3 计算一个文件有多少行
# while read val
# do
#
# #read在循环里面重定向到一个文件
# done < 1.txt
cnt=0while read line #不用 + 1是因为read会自动从上一次读取的位置的下一行开始读取
doif [ -n "$line" ]; thencnt=`expr $cnt + 1`fi
done < 1.sh
echo "1.sh 有 $cnt 行"#请你写一个shell脚本,计算你这段时间总共写了多少句代码
# 统计你的share共享文件夹里面所有的.c .h文件总共有多少行
# 如果你是按照我写的那个统计有多少行不行
# 有空白行,你需要过掉#写个函数计算一个文件里面有多少行代码
get_lines()
{cnt=0while read linedoif [ -n "$line" ];thencnt=`expr $cnt + 1`fi done < $1echo $cnt
}
r=`find /mnt/hgfs/1_share/ -regex ".*/*\.[ch]"`cnt=0
file_cnt=0
for val in $r
do#获取一个文件里面有多少有效行echo $valn=`get_lines $val`#这里在累计行数cnt=`expr $cnt + $n`#这里在累计文件个数file_cnt=`expr $file_cnt + 1`
done
echo "总共你写了 $file_cnt 个代码"
echo "总共你写了 $cnt 行代码"