Linux
命令、命令重定向和管道命令;shell
语法结构;shell
程序。实验包括预习报告和实验报告;
实验预习报告应根据课程内容,查阅相关资料,列出与实验相关的背景知识;
实验报告应包括设计方案、详细步骤、结果分析等,关键过程和运行结果可配以截图说明。
shell是一个接收由键盘输入的命令并将其传递,给操作系统来执行的程序,shell脚本是一个包含一系列命令的文件 ,shell既是命令行接口,也是脚本语言解释器,shell脚本文件的使用惯例后缀名通常使用.sh,权限通常为755和700(rw),执行脚本需显式地指定脚本的路径名,脚本中的命令尽量使用长选项,使用制表符缩进。
变量:扩展变量在必要时使用{ }来界定,约定使用大写字母表示常量;一个未明确赋值的变量仅含一个空字符串 赋值时,变量名、等号和值之间不能含有空格,当赋值包含空格、制表符、换行符时,需用引号括起来。
数组:数组由元素组成,元素使用索引(下标)来访问。使用数组名[下标]=值
赋值,使用${数组名[下标]}
进行访问,缺省下标时表示下标为0元素,*下标和@下标表示数组中所有非空元素。declare-a
数组名用来显式声明一个数组,unset
删除数组。
函数:先定义,后使用,退出值$?
变量上一命令的退出值,为0执行成功。在return
时设置函数的退出值;使用local
定义局部变量;
function name {
commands
return
}
name () {
commands
return
}
位置参数:使用$n
获取n位上的实参;$#
表示除脚本名的其他参数;使用shift
移动位置参数,每执行一次$#
的值减一,后面的值向前移动一位(shift n
表示移动n个位置参数);set
为位置参数赋值或者重新赋值;$*
和$@
表示从1开始的所有位置参数。
读取键盘输入:read
交互式地为变量赋值,可以从键盘输入也可以使用重定向从文件读入,-p string
:显示提示符,默认读到$REPLY
变量;-s
不显示输入地值。
变量扩展:基本扩展a="foo";echo"${a}_file"
;
${parameter:-word} 若parameter为空,则扩展为word的值,否则扩展为parameter的值
${parameter:=word} 若parameter为空,则扩展为word的值,并将word赋给parameter,否则扩展为parameter的值
${parameter:?word} 若parameter为空,则报错并退出,将word的内容输出到标准错误;否则扩展为parameter的值
${parameter:+word} 若parameter为空,则扩展为空,否则扩展为word的值
变量扩展:${#parameter}
扩展为parameter
字符串的长度(${#@}与${#*}
与$#
相同,返回实参的个数(不包含 0 ) ) ; 0)); 0));{parameter:offset}
与${parameter:offset:length}
提取一部分包含在parameter中的字符串(允许offset为负值,表示从字符串末尾开始,负值前必须有一个空格,避免与:-
扩展混淆);${#name[*]}与${#name[@]}
数组中非空元素的个数;${#name[n]}
数组中下标为n元素的字符串长度。
eval
与exec
对命令进行两次扫描;
整数运算符:let
与(())
用来执行整数算术运算(let
后跟的算数表达式不能包含空格,但是(())
允许使用空格,若let
命令的算法表达式包含shell
的特殊字符,必须用双引号括起来,而(())
内使用也可不使用双引号 只能用来处理整数,通过名字识别变量);使用$(())
进行算数扩展;
if commands; then
commands
[elif commands; then
commands]
[else
commands]
fi
case string in
pattern1) commands;;
pattern2) commands;;
…
*) commands;;
esac
# while语句
while condition; do
commands
done
# until语句
until condition; do
commands
done
# break n:从循环体中退出,跳出n重循环
# continue n:跳出到n层循环体并进行下一次循环
# exit n:退出脚本,设置退出值为n
# 循环变量从值表中取值,每取一个值,进入一次循环体
for variable in words; do
commands
done
animals=(”a dog” ”a cat” ”a fish”)
for i in ${animals[*]}; do echo $i; done
for i in ${animals[@]}; do echo $i; done
for i in “${animals[*]}”; do echo $i; done
for i in “${animals[@]}”; do echo $i; done
for ((e1; e2; e3)); do
commands
done
# 类似于c语言for循环结构
((e1))
while ((e2)); do
commands
((e3))
done
基本的错误类型
语法错误:违反编程语言的规则而造成的,当发生语法错误时,脚本运行失败并提示出错的位置。
逻辑错误:由于程序的逻辑关系存在的问题。1. 防御性编程 2. 输入验证 3. Design is a Function of Time
测试
Release Early, Release Often,测试与开发同样重要 。
使用桩(stub)测试程序流程。
提高测试的覆盖率。
调试
经常使用echo或print能够快速地找到问题域,发现错误,追踪脚本的运行
bash的x标志
- 在shebang中加入-x,启用bash的追踪模式,用来监 视整个脚本的运行情况
- 使用set -x和set +x可对部分代码进行追踪
- 在输出中,+起始的行表示追踪信息
Linux
命令、命令重定向和管道命令;shell
语法结构;Linux
管理任务编写shell
程序。实验包括预习报告和实验报告;
实验预习报告应根据课程内容,查阅相关资料,列出与实验相关的背景知识;
实验报告应包括设计方案、详细步骤、结果分析等,关键过程和运行结果可配以截图说明。
shell是一个接收由键盘输入的命令并将其传递,给操作系统来执行的程序,shell脚本是一个包含一系列命令的文件 ,shell既是命令行接口,也是脚本语言解释器,shell脚本文件的使用惯例后缀名通常使用.sh,权限通常为755和700(rw),执行脚本需显式地指定脚本的路径名,脚本中的命令尽量使用长选项,使用制表符缩进。
1 #!/bin/bash 2 3 num=$1 4 a1=1 5 a2=1 6 a3=0 7 sum=2 8 if [ $num -eq 1 ]; then 9 echo -ne "fib:" $a1 "\n" 10 echo -ne "sum:" 1 "\n" 11 elif [ $num -eq 2 ]; then 12 echo -ne "fib:" $a1 " " $a2 "\n" 13 echo -ne "sum:" 2 "\n" 14 else 15 echo -ne "fib:" $a1 " " $a2 " " 16 for ((i=3;i<=$num;i++)) do 17 let a3=$a2+$a1 18 echo -n $a3 " " 19 let sum=$sum+$a3 20 let a1=$a2 21 let a2=$a3 22 done 23 echo -ne "\nsum:" $sum "\n" 24 fi
输出结果
[root@iZuf6cfng5wmyxerumg92jZ ~]# ./fibo.sh 10 fib: 1 1 2 3 5 8 13 21 34 55 sum: 143
1 #!/bin/bash 2 3 parameter_number=$# 4 5 file_path=$1 6 7 # 没有任何参数 8 if [ $# -eq 0 ]; then 9 exit 1 # 退出 10 fi 11 12 # 如果第一个参数指定的目录不存在 13 if [ ! -d $file_path -a ! -f $file_path ]; then 14 mkdir $file_path # 创建目录 15 # 如果为普通文件 16 elif [ -f $file_path ]; then 17 echo 123 18 rm -f $file_path # 删除它 19 mkdir $file_path # 创建目录 20 # 如果目录存在 21 elif [ -d $file_path ]; then 22 rm -f $file_path/* # 清空 23 fi 24 if [ $# -gt 1 ]; then 25 # 循环复制文件到目录下面 26 for (( i=2; i <= $parameter_number; i++)); do 27 eval mv "\$$i" "$file_path/" 28 done 29 fi
输出结果
wy@ZeroJean:~$ touch {1..4}.txt wy@ZeroJean:~$ ls 1.txt 2.txt 3.txt 4.txt e1.sh e2.sh e3.sh e4.sh wy@ZeroJean:~$ ./e2.sh 4.txt 1.txt 2.txt 3.txt 4.txt 123 mv: cannot move '4.txt' to a subdirectory of itself, '4.txt/4.txt' wy@ZeroJean:~$ ls 4.txt e1.sh e2.sh e3.sh e4.sh wy@ZeroJean:~$ cd 4.txt wy@ZeroJean:~/4.txt$ ls 1.txt 2.txt 3.txt
/etc
目录下的所有普通文件,寻找其中最长的一行,输出其内容和长度(忽略 /etc 的所有子目录)1 #!/bin/bash|/bin/sh 2 3 max_len=0 4 max_str="" 5 6 ls -l /etc |grep ^- | while read line ; do 7 len=${#line} 8 if ((len>max_len)); then 9 max_len=$len 10 max_str=$line 11 echo $max_str >tmp 12 echo -n "len:" >>tmp 13 echo $max_len>>tmp 14 fi 15 done 16 17 echo /etc下最长文件为: 18 cat < tmp 19 rm tmp
输出结果
[root@iZuf6cfng5wmyxerumg92jZ ~]# sh null.sh /etc下最长文件为: -rw-r--r--. 1 root root 5122 Aug 8 2019 makedumpfile.conf.sample len:71
依次读取年、月、日三个参数(如1991 2 14),计算它距离今天的天数。其中,
能被4整除但不能被100整除,或能被400整除的年为闰年
使用 date +"%-Y"
, date +"%-m"
和 date +"%-d"
可获得当前的年月日
注:请编写函数并使用算术方法计算,下面三行可用来验证结果
#!/bin/bash # 如果输入的参数不是3个 if [ ! $# -eq 3 ]; then # 直接退出 exit 1 fi # 记录差 dayGap=0 monthGap=0 yearGap=0 # 获取当前日期 currentTime=`date +%Y-%m-%d` # 提取年月日 2021-12-19 year=${currentTime:0:4} month=${currentTime:5:2} day=${currentTime:8:2} # echo $year $month $day # 判断一年是否是闰年 function isLeapYear () { t=$1 let prv=$t%4 let nxt=$t%100 let aft=$t%400 if [ $prv -eq 0 -a ! $nxt -eq 0 -o $aft -eq 0 ]; then echo 1 else echo 0 fi } function Month () { pMonth=$1 pYear=$2 pDay=0 provisional=`isLeapYear $pYear` # echo "provisional=$provisional $pYear" >> tp if [ $provisional -eq 1 ]; then case $pMonth in 1) pDay=31;; 2) pDay=29;; 3) pDay=31;; 5) pDay=31;; 7) pDay=31;; 8) pDay=31;; 10) pDay=31;; 12) pDay=31;; *) pDay=30;; esac else case $pMonth in 1) pDay=31;; 2) pDay=28;; 3) pDay=31;; 5) pDay=31;; 7) pDay=31;; 8) pDay=31;; 10) pDay=31;; 12) pDay=31;; *) pDay=30;; esac fi echo $pDay } function yearDay () { dd=0 provision=$1 for ((i=1; i<=12; i++)); do let c=`Month $i $provision` # echo $c >> temp let dd=$dd+$c # echo "dd=$dd" >> temp done echo $dd } # 获取输入日期 inputYear=$1 inputMonth=$2 inputDay=$3 # echo $inputYear $inputMonth $inputDay # 两个日期在同一年 if [ $year -eq $inputYear ]; then # 同一个月 if [ $inputMonth -eq $month ]; then let dayGap=$day-$inputDay # 不同月 else provisionalYear=$inputYear let monthGap=$month-$inputMonth let $monthGap=$monthGap-1 for (( i=$monthGap; i>0; i--)); do let temp=$inputMonth+$i let dayGap=$dayGap+`Month $temp $provisionalYear` done a=`Month $inputMonth $provisionalYear` let a=$a-$inputDay let dayGap=$dayGap+$a let dayGap=$dayGap+$day fi # 不同年 else # 计算输入年份和现在年份之间空白年的总天数 let yearGap=$year-$inputYear # echo "yearGap=$yearGap" >> temp for ((i=$yearGap-1; i>0; i--)); do let provisionalYear=$inputYear+$i # echo "provisionalYear=$provisionalYear" >> temp for ((j=1; j<=12; j++)); do a=`Month $j $p provisionalYear` let dayGap=$dayGap+$a done done # 计算现在年份还有多少天 provisionalYear=$year for ((i=1; i<$month; i++)); do let a=`Month $i $provisionalYear` let dayGap=$dayGap+$a echo "dayGap=$dayGap" >> temp done let dayGap=$dayGap+$day # 计算输入年剩余天数 provisionalYear=$inputYear a=0 for ((i=1; i<$inputMonth; i++)); do let b=`Month $i $provisionalYear` let a=$a+$b # echo "dd1=$a" >> temp done let a=$a+$inputDay let b=`yearDay $inputYear` # echo $b let b=$b-$a let dayGap=$dayGap+$b fi echo "相距$dayGap天"
输出结果
aistudio@jupyter-301017-3228551:~$ ./time.sh 2019 02 03 相距 1050天
检验程序
#!/bin/bash timestamp1=`date +%s -d $1-$2-$3` timestamp2=`date +%s` echo $((($timestamp2 - $timestamp1)/86400))
检验程序输出结果
aistudio@jupyter-301017-3228551:~$ ./timeCheck.sh 2019 02 03 1050
通过本次实验熟悉并练习了Linux
命令、命令重定向和管道命令使用,并运用shell
的扩展、条件判断以及循环语句的语法且根据实验要求所设计的实际的linux
管理任务编写了shell
程序。在实验中对学习中产生的相关困惑和疑问得到了很好的处理,收获颇多。