Shell 编程

1. 概念

Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(command interpreter,命令解析器)。它类似于DOS下的COMMAND.COM和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。
同时它又是一种程序设计语言。作为命令语言,它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令;
作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支

1.1 简述

shell是什么:解析执行用户的命令 命令解析器 ,让命令具有流程控制能力
支持交互式(interactive 逐句执行)
支持批处理(batch 写一个shell脚本 shell顺序执行)
与编程类语言相识 有变量和流程控制 但是解释性语言

1.2 sheel 的版本

UNIX系统上有多种shell

sh(bourne shell) 由 steve bourne开发 nuix内置
csh(c shell) 由bill joy开发 shell升级版 支持作业控制 命令历史 命令行编译
ksh(korn shell) 由david korn开发 sh+csh
tcsh(tenex c shell) 是csh升级版 有命令补全功能
bash(bourne again shell) sh的扩展

查看当前 sheel版本
echo $SHEEL
/bin/bash

2. 创建并执行一个 sheel文件

2.1 感受批量处理

[root@nt 桌面]#  (date;ll;echo 1+1)  # 命令之间用;分割
2021年 12月 20日 星期一 19:25:02 CST
总用量 68
-rwxr--r--. 1 root root  174 12月 18 12:15 1.sh
-rw-r--r--. 1 root root  740 12月 20 14:42 array1.sh
-rw-r--r--. 1 root root  117 12月 20 17:10 array2.sh
-rw-r--r--. 1 root root  362 12月 18 15:00 case1.sh
-rw-r--r--. 1 root root  295 12月 20 10:56 case2.sh
-rw-r--r--. 1 root root  123 12月 18 15:17 for1.sh
-rw-r--r--. 1 root root  412 12月 18 15:21 for2.sh
-rw-r--r--. 1 root root  208 12月 18 15:32 for3.sh
-rw-r--r--. 1 root root  936 12月 20 11:15 for4.sh
-rw-r--r--. 1 root root  619 12月 20 11:37 for5九九乘法表.sh
-rw-r--r--. 1 root root  940 12月 20 14:24 for6.sh
-rw-r--r--. 1 root root  351 12月 20 14:21 for7.sh
-rw-r--r--. 1 root root 1047 12月 20 16:57 function1.sh
-rw-r--r--. 1 root root  116 12月 18 14:06 if1.sh
-rw-r--r--. 1 root root  197 12月 18 14:42 if2.sh
-rw-r--r--. 1 root root  410 12月 20 10:58 if3.sh
-rw-r--r--. 1 root root  482 12月 20 14:30 while1.sh
1+1

2.2 定义变量

给变量赋值,等号前后不能有空格。
$变量名,获取变量值

[root@nt 桌面]#  i=1;echo $i;  # 给变量赋值 等号前后不能有空格   #$变量名:获取变量值
1

2.3 创建 sheel文件

[root@]# touch 1.sh   #创建空文件
[root@]# vim 1.sh     #编辑文件
[root@]# ll           #查看文件列表
总用量 8
-rw-r--r--. 1 root root   43 12月 18 09:31 1.sh
-rw-------. 1 root root 1232 12月 17 10:24 anaconda-ks.cfg
[root@]# cat 1.sh     # 查看文件内容
# /bin/bash
date;
i=1;
echo $i;
ls -l;
[root@]# chmod u+x 1.sh    #授予当前用户对1.sh的执行权
[root@]# ll
总用量 8
-rwxr--r--. 1 root root   43 12月 18 09:31 1.sh
-rw-------. 1 root root 1232 12月 17 10:24 anaconda-ks.cfg

2.4 执行 sheel文件

方式一:./sheel文件
方式二:shource sheel文件
方式三:/bin/bash sheel文件
方式四:sh sheel文件

[root@]# ./1.sh
2021年 12月 18日 星期六 09:34:12 CST
1
总用量 8
-rwxr--r--. 1 root root   39 12月 18 09:34 1.sh
-rw-------. 1 root root 1232 12月 17 10:24 anaconda-ks.cfg
[root@]# source 1.sh
2021年 12月 18日 星期六 09:34:39 CST
1
总用量 8
-rwxr--r--. 1 root root   39 12月 18 09:34 1.sh
-rw-------. 1 root root 1232 12月 17 10:24 anaconda-ks.cfg
[root@]# /bin/bash 1.sh
2021年 12月 18日 星期六 09:34:50 CST
1
总用量 8
-rwxr--r--. 1 root root   39 12月 18 09:34 1.sh
-rw-------. 1 root root 1232 12月 17 10:24 anaconda-ks.cfg

3. window 与 Linux实现文件互传

3.1 通过 rzmingl

安装 rz
yum install lrzsz

通过 xftp文件

4. shell编程

4.1 数据类型

字符串类型:双引号、单引号
整数:注意 boolean用0和1表示
浮点数

4.2 语法

以分号表示语句结束 换行也可以表示语句结束
变量命名:由数字 字母 下划线组成,不能以数字开头 ,不能是shell关键字,区分大小写

4.3 变量

给变量赋值

[root@java37]# i=1;  #等号两边不能有空格
[root@java37]# i= 1
-bash: 1: 未找到命令
[root@java37]# i =1
-bash: i: 未找到命令

输出变量

[root@java37]# echo $i;   #输出变量i的值 并换行
1
[root@java37]# printf $i;  #输出变量i的值 但不换行
1[root@java37]# 

$使用

1[root@java37]# i=1;j=$i;echo $j;   #$i  :获取变量i的值
1
[root@java37]# j=${i};echo $j;      #${i}  :获取变量i的值
1
[root@java37]# j=$((i+1));echo $j;  #$((式子))  :进行数学运算
2
[root@java37]# j=$[i+1];echo $j;    #$[式子]    :进行数学运算
2
[root@java37]# j=$(date);echo $j;   #$(命令)    :执行语句 并获取结果
2021年 12月 18日 星期六 09:59:11 CST

通过键盘给变量赋值:read 函数

[root@java37]# read -p "请输入一个整数:" i;
请输入一个整数:3
[root@java37]# echo $i;
3

4.4 运算

[root@java37]# i=2;
[root@java37]# j=$((1+i));echo $j;   #通过$((式子))
3
[root@java37]# j=$[1+i];echo $j;    #通过$[式子]
3
[root@java37]# j=$(expr 1+1); echo $j;  #获取的是字符串
1+1
[root@java37]# j=$(expr 1 + 1); echo $j; #有空格 才进行运算
2
[root@java37]# j=$(expr 1 + i); echo $j;  #必须是常量值
expr: 非整数参数

[root@java37]# j=$(expr 1 + $i); echo $j;  #$i获取的就是值了
3
[root@java37]# j=`expr 1 + $i`; echo $j;   #列表必须是破折号  在1旁边
3

[root@java37]# j=$(expr 3 \* 2 ); echo $j;  #乘法运算
6
[root@java37]# j=$(expr 3 / 2 ); echo $j;   #除法运算
1
[root@java37]# j=$(expr 3 % 2 ); echo $j;   #求余运算
1

4.5 运算符

数字运算符

-eq 等于
-ne 不等于
-gt 大于
-ge 大于等于
-lt 小于
-le 小于等于

字符串运算符

= 判断是否相等
!= 判断是否不同
-n 是否为空

逻辑运算符

! 取反
-a 与
-o 或

4.6 计算浮点数

[root@java37]# i1=13.5;i2=3.3;
[root@java37]# echo "$i1 / $i2" | bc;
4
[root@java37]# echo "scale=3;$i1 / $i2" | bc;  #scale=3设置小数点位置
4.090

5. 流程控制

5.0 注意

$变量可以取出定义的值。在双引号里也可以使用 $变量进行取值,但是在单引号中 $变量不可以取出值.

5.1 条件判断

#条件判断1:test 条件表达式  #注意比较运算符前后必须有空格
[root@java37]# test 1 -gt 2; 
# $? 获取上一个语句的结果
[root@java37]# echo $?;
1

#条件判断1:[ 条件表达式 ]   :注意[]两边必须有空格
[root@java37]# [ 1 -lt 2 ];
[root@java37]# echo $?;
0

5.2 选择结构

1. 单分支

格式:
if 条件判断 ; then
    statement;
fi;
[root@java37]# if [ $sex="男" ];then
> echo "sex="$sex",帅哥!";
> fi;
sex=男,帅哥!


[root@java37]# if test $sex="男"; then
> echo "sex="$sex",帅哥!";
> fi;
sex=男,帅哥!

2. 双分支

双分支格式:
if 条件判断; then
    statement1
else
   statement2
fi;
# 测试if 双分支
read -p  "请输入你的性别:" g;
if test $g = "男"; then
        echo "你是帅哥";
else
        echo "你是美女";
fi;
#判断一个年是不是闰年
read -p "请输入年份:" n;
if [ $((n%4)) -eq 0 -a $((n%100)) -ne 0 -o $((n%400)) -eq 0 ];then
    echo $n"年:是闰年";
else
    echo $n"年:不是闰年";
fi;
# 判读一个数是不是水仙花数
read -p "请输入一个三位数:" n;
# 判断是不是三位数
if [ $n -ge 100 -a $n -le 999 ];
then
  # 获取各个位数的值
  n1=$(( n%10 ));
  n2=$(( n/10%10 ));
  n3=$(( n/100 ));
  sum=$(( n1*n1*n1+n2*n2*n2+n3*n3*n3));
  if [ $sum -eq $n ];
  then
    echo $n"是水仙花数。";
  fi;
    echo $n"是三位数。";
else
  echo "$n不是三位数";
fi;

5.3 选择结构 case

case 格式

case格式:
case 变量 in
      值1)
              statement1;
              statement2;;
      值2)
               statement1;
               statement2;;
      值3)                        #值3)   类似于case 值3:
               statement1;
               statement2;;      #;;  类似于break
      *)                         # *) 类似于default
               statement1;
               statement2;;
esac; 
#输入季节输出月份
read -p "请输入季节:" season;
case $season in
  "春")
    echo $season"季,对应的月份是3 4 5.";;
  "夏")
    echo $season"季,对应的月份是6 7 8.";;
  "秋")
    echo $season"季,对应的月份是9 10 11.";;
  "冬")
    echo $season"季,对应的月份是12 1 2.";;
  *)
    echo $season":输入错误^-^.";;
esac;
read -p "判断星期几:" n;
case $n in
  1)
    echo $n"星期一";;
  2)
    echo $n"星期二";;
  3)
    echo $n"星期三";;
  4)
    echo $n"星期四";;
  5)
    echo $n"星期五";;
  6)
    echo $n"星期六";;
  7)
    echo $n"星期日";;
  *)
    echo $n"输入错误!";;
esac;

5.4 循环结构 for

for 结构

for 变量 in 范围/数组;do
    statement;
done;
#输入一个数,循环这个数次
read -p "请输入循环的数量:" n;
for ((i=0;i <= $n;i++))
do
  echo "o^O";
done;
#!/bin/bash
#从1加到100的值输出显示,如何把 100做成一个变量
#定义一个变量 sum
sum=0
for (( i=1; i<=100; i++))
do
#写上你的业务代码
  sum=$[$sum+$i]
done
echo "总和sum=$sum"

1. for 的几种用法

#for练习获取 1-10的和
sum=0;
for n in 1 2 3 4 5 6 7 8 9 10;
do
 sum=$(($sum+n));
done;
echo "获取1-00的值为:$sum";

echo "第二种======================";
sum=0;
for n in {1..10};
do
 sum=$((sum+n));
done;
echo "获取1-10的值为:$sum";

echo "第三种======================";
sum=0;
for ((n=1;n<=10;n++));
do
  sum=$((sum+n));
done;
echo "获取1-10的值为:$sum";

echo "第四种seq 一个参数======================";
sum=0;
for n in $(seq 10); # 一个参数:尾数
do
  sum=$((sum+n));
done;
echo "获取1-10的值为:$sum";

echo "第四种seq 二个参数======================";
sum=0;
for n in $(seq 1 10); # 两个参数:首数 尾数
do
  sum=$((sum+n));
done;
echo "获取1-10的值为:$sum";

echo "第四种seq 三个参数======================";
sum=0;
for n in $(seq 1 1 10); # 三个参数:首数 增量 尾数
do
  sum=$((sum+n));
done;
echo "获取1-10的值为:$sum";

2. 九九乘法表

# 99乘法表1
for ((i=1;i<=9;i++));
do
  for ((j=1;j<=i;j++));
  do
    printf $i"*"$j"="$((i*j))"  ";
    if [ $((i*j)) -lt 10 ]; # 如果是小于10多打一个空格
    then
      printf " ";
    fi;
  done;
  echo ""; # 换行
done;
## 99乘法表
for ((i=1;i<=9;i++));
do
  for ((j=9;j>=1;j--));
  do
    if [ $j -le $i ]; # 如果 j大于i就打印公式,否则打印空格
    then
      printf $i"*"$j"="$((i*j))"  ";
      if [ $((i*j)) -lt 10 ];
      then
        printf " "; # 如果是小于10多打一个空格
      fi;
    else
        printf "        "; # 八个空格 
    fi;
  done;
  echo ""; # 换行
done;

3. 100以内的质数之和

# 打印1-100内的质数的和
sum=0;
for ((n=1;n<=100;n++));
do
  # 判断当前n是不是质数
  b=1; # 定义变量作为标签 
  # 定义一个变量让其从2跑到n-1 循环判断是否除尽
  for ((m=2;m<(n/2+1);m++)); # 判断 n能否被某个数整除,否则就判断它是质数
  do
    if [ $((n%m)) -eq 0 ];
    then
      b=$((b+1)); # 如果被整除 b+1
      break;
    fi;
  done;

  if [ $b -eq 1 -a $n -ne 1 ]; # 如果 b没有变化就说明它是质数
  then
    echo $n"是质数";
    sum=$((sum+n));
  fi;
done;
echo "1-100内质数的和是:$sum"
---------------------
umn=0;
for ((n=1;n<=100;n++));do
   #判断当前n是不是质数
   #定义一个变量让其从2跑到n-1 循环判断是否出尽
   for ((m=2;m<n;m++)) do
       if [ $((n%m)) -eq 0 ];then
                        break;
           fi;
   done;
   if [ $m -eq $n ];then
       echo $n"是质数!";
           sumn=$((sumn+n));
   fi;
done;
echo "求1到100内质数的和2="$sumn;

5.5 循环结构 while

while 格式

while 判断语句; do
    statement;
done;
# 使用while获取1到100内位数上含有1的所有质数的平均值
sumn=0;geShu=0;
n=1;
while [ $n -le 100 ]; do
    #判断是否含有1
	n1=$((n%10));
	n2=$((n/10%10));
	n3=$((n/100));
	if [ $n1 -eq 1 -o $n2 -eq 1 -o $n3 -eq 1 ];then
	     #判断当前n是不是质数
         m=2;
         while [ $m -lt $n ];do
              if [ $((n%m)) -eq 0 ];then
			       break;
			  fi;
			  m=$((m+1));
      	 done;	
         if [ $m -eq $n ]; then
                sumn=$((sumn+n));
				geShu=$((geShu+1));
				echo $n"是满足条件的数字!";
         fi; 		 
	fi;
	n=$((n+1));
done;
echo "1到100内位数上含有1的所有质数的平均值="$((sumn/geShu));
echo $sumn":"$geShu"1到100内位数上含有1的所有质数的平均值="$(echo "scale=2;"$sumn"/"$geShu|bc);
echo $sumn":"$geShu"1到100内位数上含有1的所有质数的平均值="$(echo "scale=2;$sumn/$geShu"|bc);

n=1;
echo $n"---";  #获取变量的值$n在字符串中也可以解析
echo "---$n---";
# 输入三位密码,只有5次机会,如果不是123 密码错误
cishu=0;
m=0;
while [ $cishu -lt 5 ];
do
  cishu=$((cishu+1));
  read -p "请输入密码:" n;
  if test $n -eq 123;
  then 
    m=$((m+1));
    break;
  fi;
done;
if [ $m -eq 1 ];
then
  echo "经过$cishu后得到正确的密码。";
else
  echo "输入次数5次,请明天再试。";
fi;

6. 数组

6.1 简单使用

# 数组,记录多个数据
array1=(1 3 45 "abv" true 5 23 -1 34 19 34);
echo '$数组:获取的是下标为0的元素:'$array1;
echo '${数组}:获取的是下标为0的元素:'${array1};
echo '${数组[0]}:获取的是下标为0的元素:'${array1[0]};
echo '${数组[*]}:获取所有元素:'${array1[*]};
echo '${数组[@]}:获取所有元素:'${array1[@]};
echo '${#数组[*]}:获取数组长度:'${#array1[*]};
echo '${#数组[@]}:获取数组长度:'${#array1[@]};
echo '${数组[2]}:获取的是下标为2的元素:'${array1[2]};
echo '${数组[下标]}:获取的是下标为n的元素:'${array1[4]};
array1[0]=100; # 给下标为0的元素赋值
echo '${数组[下标]}:获取下标为n的元素:'${array1[0]};

6.2 获取最大值

# 获取元素最大值
arr2=(1 3 5 8 9 10 12 2 3 5);
max2=${arr2[0]};
echo $max2;
for n in ${arr2[@]}; do
    if test $max2 -lt $n;then
		max2=$n;
	fi;
done;
echo ${arr2[*]}'的最大值是:'$max2;

6.3 排序

#排序
arr2=(1 3 5 8 9 10 12 2 3 5);
echo ${arr2[*]};
for ((i=0;i<$((${#arr2[*]}-1));i++));do
    for ((j=$((i+1));j<${#arr2[*]};j++));do
        if test ${arr2[$i]} -gt  ${arr2[$j]}  ;then
                    k=${arr2[$i]};
					arr2[$i]=${arr2[$j]};
					arr2[$j]=$k;
        fi;		
    done;	
done;
echo ${arr2[*]};

for ((i=0;i<${#arr2[*]}-1;i++));do
    for ((j=i+1;j<${#arr2[*]};j++));do
        if test ${arr2[i]} -lt  ${arr2[j]}  ;then
                    k=${arr2[i]};
					arr2[$i]=${arr2[j]};
					arr2[$j]=$k;
        fi;		
    done;	
done;
echo ${arr2[*]};

7. 函数

shell函数格式:没有返回值类型+参数列表

[function] 函数名 [()]{
  action;
  [return 返回值;]
}
# sheel 函数格式:没有返回值类型+参数列表
# 格式:
: '
我是注释
[function] 函数名 [()]{
  action;
  [return 返回值;]
}
$# 传递到脚本或函数的参数个数
$* 以一个单字符串显示所有向脚本传递参数
$$ 脚本运行的当前进程 id号
$! 后台运行的最后一个进程的 i号d
$@ 与 $*相同,但是使用时加引号,并在引号中返回每个参数。
$- 显示 sheel使用的当前选项,与 set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
'
function twoSum(){
 echo '$0='$0; # 获取执行命令
 echo '$1='$1; # 获取第一个参数 4
 echo '$2='$2; # 获取第二个参数 5
 echo '$?='$?; # 获取命令退出状态。0
 echo '$*='$*; # 获取所有参数 4 5 6
 echo '$@='$@; # 获取所有参数 4 5 6
 echo '$#='$#; # 获取参数列表的个数
 echo '$$='$$; # 获取进程id
}
twoSum 4 5 6; # 调用函数

function foreach(){
 for i in $*;
 do
  echo "参数是:"$i;
 done;
}
foreach 9 30 4 5; # 调用函数

7.1 练习

function add1(){
   sumab=$(($1+$2));
   return $sumab;
}
add1 3 4;
sum1=$?;#获取上一个命令的结果
echo "3+4="$sum1;


#遍历获取所有的参数
function test2(){
  geShu=1;
  for i in $*;do
      echo '第'$geShu'个参数的值是:'$i;
	  geShu=$((geShu+1));
  done;
}
test2 2 9 7 1 6;


#* 判断一个数是不是质数
function pdZhiShu(){
   if [ $1 -le 1 ]; then
        return 1;
   fi;
   for ((m=2;m<$1;m++));do
        if [ $(($1%m)) -eq 0 ];then
		      return 1;
		fi;
   done;
   return 0;#返回true/false不行
}
#调用
for ((n=0;n<=10;n++));do
   pdZhiShu $n;
   result=$?;
   if [ $result -eq 0 ];then
       echo $n'是质数';
   fi;
done;

#* 判断参数年参数月有多少天
function test3(){
    days=0;
    year=$1;month=$2;
	case $month in
	     4 | 6 | 9 | 11)
           days=30;;	
		 2)
           if [ $((year%4)) -eq 0 -a $((year%100)) -ne 0 -o $((year%400)) -eq 0 ];then
                  days=29;
           else
		          days=28;
           fi;;
         *)
           days=31;;
	esac;
	return $days;	     		   
}
for ((year=2000;year<=2020;year++));do
    for ((month=1;month<=12;month++));do
	      test3 $year $month;
          echo $year'年'$month'月有'$?'天!';		  
	done;
done;
#* 证明所有四位数进入黑洞的次数<=7 
function heiDong(){
   for ((n=1000;n<10000;n++));do
       if [ $((n%1111)) -eq 0 ];then
	       continue;
	   fi;
	   k=$n;
	   ciShu=0;
	   while [ $k -ne 6174 ];do
	       #获取各个位数的值
		   arr=($((k%10)) $((k/10%10)) $((k/100%10)) $((k/1000)));
		   #echo ${arr[*]};
		   #数组排序
		   for ((i=0;i<${#arr[*]}-1;i++));do
		        for ((j=i+1;j<${#arr[*]};j++));do
				      if [ ${arr[i]} -gt ${arr[j]} ];then
		                 temp=${arr[i]};arr[i]=${arr[j]};arr[j]=$temp;
					  fi;	 
		        done;
		   done;
		   #echo ${arr[*]};
		   maxArr=$((${arr[3]}*1000+${arr[2]}*100+${arr[1]}*10+${arr[0]}*1));
		   minArr=$((${arr[0]}*1000+${arr[1]}*100+${arr[2]}*10+${arr[3]}*1));
		   k=$((maxArr-minArr));
		   ciShu=$((ciShu+1));
	   done;
	   echo $n'经过'$ciShu'次运算后得到'$k;
   done;
}
#heiDong;


#* 斐波那契数列:写一个方法获取第n个数: 1 1 2 3 5 8 13 21
function fbnq1(){
   arg=$1;
   #使用数组
   arr=(1 1);
   for ((i=2;i<arg;i++));do
      arr[$i]=$((${arr[i-1]}+${arr[i-2]}));  
   done;
   echo ${arr[*]};
   #echo '${arr[arg-1]}'${arr[arg-1]}'---$((arg-1))'$((arg-1));
   resultFbnq=${arr[arg-1]};
}
for ((n=1;n<=20;n++));do
   fbnq1 $n;
   echo   '第'$n'个元素是:'$resultFbnq;
done;

7.2 注意 return 的作用

#探究result的返回值:return后面的值 不是返回值是状态码:从0到255
function test3(){
   return 1597;
}
test3;echo '1597='$?;

#设置返回值值的正确方式
function test4(){
   result4=$(($1+1));
}
test4 3;
echo '3+1='$result4;