shell编程

shell是什么?
        是一个命令解析器(程序),可以解析用户的输入,交给操作系统内核执行,并且把结果反馈给用户,本身也是一个程序

我们可以把很多需要执行的命令(操作),以一种语言的方式组织起来,交给shell去运行(解析)
========>

shell脚本语言

命令解析器(shell)本质也是一个程序
        cat /etc/shells   

# /etc/shells: valid login shells
/bin/sh
/bin/bash           <---------
/bin/rbash
/bin/dash

上面都是shell脚本的解释器程序

编译型语言(C/C++)
解释型语言(shell,python,js)

一、写第一个最简单的脚本文件

shell脚本文件以.sh结尾

#!/bin/bash
echo "hello shell" 

第一行都是固定的:  #!/bin/bash 

指定由/bin目录下面的哪一个解释器来解析这个脚本文件
脚本中以#开头的行是注释(第一行除外)

shell脚本是不需要编译的,可以直接解释运行,但是需要可执行权限
        chmod +x 01.sh 

注意:不要在windows下创建shell脚本文件(01.sh),可能编译不能通过,在虚拟机中使用               vim创建

二、shell中变量的使用

shell变量没有类型的概念,都是字符串

变量也不需要定义,可以直接使用

变量名=值           #=两边不能有空格
name=zhangsan
x=123
x=abc
...

如何引用变量:

        $变量名    or   ${变量名}

shell变量有三种:

        1. 自定义变量(自己定义的变量)

            如:
            name=zhangsan
            x=123
            x=abc
            ...

            shell中可以使用普通的shell命令
            d=`date`   反撇号(Esc的下面)
           作用:引用反撇号里面那一个“变量/命令/程序”的输出结果(原先输出到标准输出的内容)

           也可以是程序:xxx=`/mnt/hgfs/文件共享/笔记-代码/1`
           shell中的变量都是字符串,可以直接写到一起,表示连接

#!/bin/bash 
x=zhangsan
y=lisi
z=$x$y
echo $z

        2. 位置变量

            相当于C语言里面的argc和argv
            指的是传递给脚本的参数或者传递给函数的参数,参数是按照传递位置排列的
            如:
            ./01sh  123  abc  456
            123  abc  456  就是传递给脚本的参数

            $0      第0个参数,命令行的程序名称(./01sh)
            $1,$2,$3....${10}  表示第一个到第10个参数

            $#  表示命令行参数的个数(不包括脚本的名称)

            $@  表示所有的命令行参数(一个列表),以空格隔开
                $@ =======> "$1" "$2" "$3" ....
            $*  表示所有的命令行参数(一个数据),以空格隔开
                $* =======> "$1 $2 $3 ...."

            $?  表示前一个命令/函数/程序的退出码(返回值)

        3. 环境变量:系统中一些其他程序需要的默认的变量
            shell解释器可以引用一些外部的变量(不需要定义,可以直接使用)
            如:
                HOME : 用户的家目录的路径 

                echo $HOME    ===> /home/china
                ....
                需要重点记住的:
                LD_LIBRARY_PATH         动态库的搜索路径
                PATH                    命令或者可执行程序的搜索路径


            有没有发现一个问题?
            ls也是一个程序,cd也是一个程序,自己编译出来的a.out也是一个程序
            为什么我们在运行ls和cd的时候,不需要指定路径,系统就可以找到这个程序并且运行,而我们自己写的a.out就必须指定路径,否则找不到程序

            shell会自动到指定的路径下面去查找你输入的命令,如果找到了就执行,如果找不到就报错
            "路径": PATH 
            echo $PATH
/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:/home/china:

/usr/local/arm/gcc-linaro-6.5.0-2018.12-x86_64_arm-linux-gnueabi/bin/

            指定系统要搜索的命令和可执行文件的目录列表
            PATH:dir1:dir2:.....

            我们也可以修改PATH的值,修改环境变量都是使用export
            export 环境变量的名称=值
            如:
            export PATH=$PATH:/mnt/hgfs/CS2406F/2024-1-15Linux基础/code


            export PATH=/mnt/hgfs/文件共享/笔记-代码/@数据结构/7-查找算法
                    echo $PATH

                    /mnt/hgfs/文件共享/笔记-代码/@数据结构/7-查找算法   
            

            如果是在终端输入,只对当前终端有效,终端关闭之后,环境变量的值就失效了
            如果你想要环境变量永久有效,直接写入到系统的配置文件:
            1. ~/.bashrc  
                此配置只对当前用户有效
                每一次打开终端都会执行这个脚本文件,只需要把export那一句加入到文件的后面就可以了

            2. /etc/profile
                此配置对所有用户有效,每一次开机都会执行这个脚本文件,只需要把export那一句加入到文件的后面就可以了

            3. /etc/environment    <--------
                此配置对所有用户有效,只需要把export那一句加入到文件的后面就可以了

如果觉得编译器的命令太长了,给arm-none-linux-gnueabi-gcc创建一个软链接

        arm-none-linux-gnueabi-gcc

        ------->

        arm-linux-gcc

        
ln  -s  arm-none-linux-gnueabi-gcc   arm-linux-gcc

三、shell数组

同样不需要定义,可以直接使用,不能一次性赋值,等号右边都是字符串
用法:
    数组名[下标]=value
         下标: 0,1,2,3,4,5....n-1

    引用方式:
         ${数组名[下标]}
    引用数组中所有的元素:
         ${数组名[*]}
         ${数组名[@]}
    引用数组的第一个元素:
         ${数组名}

#!/bin/bash

a[7]={a,b,c,d,e,f,g}

a[1]=a
a[2]=b

echo ${a[1]}   #--->a
echo ${a[2]}   #--->b

echo ${a[*]}   #--->a b {a,b,c,d,e,f,g}
echo ${a[@]}   #--->a b {a,b,c,d,e,f,g}

echo ${a}      #--->没有输出

四、shell中的语句

1. 说明性语句
        以#开头的行是说明性语句(注释),第一行除外

2. 功能性语句
        一般是指任意的操作系统shell命令(如:ls echo cd date ...),自己写的程序
        上面几种可以直接运行,也可以使用反撇号获取结果

        shell内部的命令(read,expr,test....)
            read: 在shell中表示从终端获取输入,保存到指定的变量
            如:
            read var1 var2
           从终端获取两个变量(字符串),第一个字符串保存在var1中,第二个字符串保存在var2中

            输入----->键盘(标准输入)
            输出----->终端/控制台(标准输出)
            输入和输出可以重定向到指定的位置
            1) 输入重定向
                命令的输入请求通常是向标准输入设备(键盘...)提交请求
                我们也可以很方便的把输入重定向到一个文件,这种情况叫做输入重定向
                方法:
                    在命令后面添加 < filename
                    该命令的所有输入都来自于filename指定的文件

            2) 输出重定向
                命令(程序)的输出通常是提交到标准输出文件(终端/控制台)
                我们也可以很方便的把输出重定向到一个文件,这种情况叫做输出重定向
                方法:
                    在命令的后面添加 > filename
                    or
                    在命令的后面添加 >> filename

                    >: 会清空原先1.txt的内容,然后把命令的输出内容写入到1.txt文件
                  >>: 追加输出,不会清空原先1.txt的内容,把命令的输出内容写入到1.txt文件的后面

                          find ./ -name 1.c > 1.txt

                          find ./ -name 1.c >> 1.txt

                           ./1 > 1.txt

             3) 命令中的管道用法
                 |
                 用法:
                     命令1 | 命令2
                     命令1的输出内容作为命令2的输入
                 如:
                     grep -nar main   查找当前目录中所有文件有main的行
                     grep -nar int   查找当前目录中所有文件有int的行

                     grep -nar main | grep int

            😭如:
                     awk -F: '{print $1}'  /etc/passwd

                    -F: 指定列的分隔符
                    $1: 是分隔符分割之后的第一个字段(列)
                    /etc/passwd  你要处理的文件名
                    '{print $1}'  你要执行的动作

                    awk  -F:  '{print $1 $3}'  /etc/passwd | grep china
                    在所有用户中查找名字叫做china的用户的UID

expr: 执行算术运算符命令
                expr主要用来进行简单的整数运算
                    +  - * / %
                如:
                    expr 5 + 3  (运算符两边,前后至少一个空格)
                    计算5+3的结果输出到标准输出

                    expr 5+3  ---->输出5+3
                    expr 5 - 3
                    expr 5 \* 3   *在shell中是一个通配符
                    expr 5 / 3  --->输出1
                    expr 5 % 3

                也可以把计算之后的结果赋值给指定的变量
                ret=`expr 5 + 3`
                i=6
                ret=`expr $i + 5 \* 3`

test: test语句可以测试三种对象
                字符串  整数  文件
                a. 字符串测试
                    =测试两边的字符串内容是否完全相同
                        =的两边至少要有一个空格
                        如:
                            test "abc" = "abc"
                            echo $?     获取上一次命令的结果

                        结果:
                            相等结果为0
                            不相等结果为1

                        可以比较两个变量是否相同
                            str1=abc
                            str2=abc
                            test $str1 = $str2
                            echo $?

                        如果比较的时候,有一个字符串为空,就会出现语法错误
                            str1=abc
                            str2=
                            test $str1 = $str2  #test "abc" =     报错
                            echo $?

                            小技巧:
                            str1=abc
                            str2=
                            test ${str1}a = ${str2}a 
                            echo $?

                    !=  测试两个字符串的内容是否不相同
                        不相同返回0,相同返回1
                        test "abc" != "abc"
                        echo $?

                    -z   zero测试字符串是否为空
                          为空就返回0
                          test -z ${str2}

                          echo $?

        

                    -n    not null  测试字符串是否不为空

                           不为空返回0

                           test -n "abc"

                           echo $?     #0

                    总结:符合你描述的条件,返回0,不符合你描述的条件,就返回1

                b. 整数测试
                    #-eq    equal  测试两个整数是否相等,相等返回0
                            test 3 -eq 4
                            echo -eq:$?    #1

                            var1=123
                            var2=123
                            test $var1 -eq $var2 
                            echo -eq:$?    #0

                    #-ne  not equal  测试两个整数是否不相等,不相等返回0
                            test 3 -ne 4
                            echo -ne:$?    #0
                            test $var1 -ne $var2 
                            echo -ne:$?    #1

                    #-gt   greate than  测试一个整数是否大于另一个整数
                            test 3 -gt 4
                            echo -gt:$?    #1

                            var1=123
                            var2=12
                            test $var1 -gt $var2 
                            echo -gt:$?    #0

                    #-ge    greate and equal     测试一个整数是否大于等于另一个整数
                            test 3 -ge 4
                            echo -ge:$?    #1

                            var1=123
                            var2=123
                            test $var1 -ge $var2 
                            echo -ge:$?    #0

                    #-lt   little than  测试一个整数是否小于另一个整数
                            test 3 -lt 4
                            echo -lt:$?    #0

                            var1=123
                            var2=12
                            test $var1 -lt $var2 
                            echo -lt:$?    #1

                    #-le    little and equal     测试一个整数是否小于等于另一个整数
                            test 3 -le 4
                            echo -le:$?    #0

                            var1=123
                            var2=12
                            test $var1 -le $var2 
                            echo -le:$?    #1

                c. 文件测试
                    -d  filename    测试filename是否为一个目录文件(directory)
                    -f  filename     测试filename是否为一个普通文件
                    -L  filename    (大写)测试filename是否为一个链接文件(硬链接是一个普通文件)
                    -p  filename    测试filename是否为一个管道文件
                    -s  filename    测试filename是否为一个套接字(socket)文件
                    -c  filename    测试filename是否为一个字符设备文件
                    -b  filename    测试filename是否为一个块设备文件

                    test -d $1
                    echo $?


                    -r  filename    测试filename是否存在并且可读
                    -w  filename    测试filename是否存在并且可写
                    -x  filename    测试filename是否存在并且可执行
                    test -r filename
                    echo $?

                    test file1 -nt  file2              newer than 
                    测试file1是否修改时间在file2的后面(测试file1是否比file2新)

                    
                    test file1 -ot  file2             old than 
                    测试file1是否修改时间在file2的前面(测试file1是否比file2旧)

            test命令可以使用[]来缩写
                test Expression  <=========> [  Expression  ]
                如:
                [ $str1 = $str2 ]
                echo $?
                [ 和 ] 两边都必须有空格

            test的复合表达式
                相当于C语言的逻辑表达式(&&  ||)
                组合了两个或者两个以上的表达式叫做复合表达式
                使用方式:
                a. 可以使用test内置的操作符来创建复合表达式
                   
test Expression1  "操作符"  Expression2
                    操作符:
                        -a     and   并且
                            全部满足才返回0
                        -o     or    或者
                            任意一个满足就返回0
                 如:
                    测试..是否为目录的同时,测试位置变量$1和$2是否相等
                    test -d ".." -a $1 -eq $2
                    echo $?
                    ===========>
                    [ -d ".." -a $1 -eq $2 ]
                    echo $?

               b. 使用条件操作符来表示复合表达式

                    test  expression1  "条件操作符"  test  expression2

                    条件操作符(&&  ||)

                           &&   并且

                            ||     或者 

                    test -d ".." &&  test  $1 -eq $2
                    echo $?
                    ===========>
                    [ -d ".." -a ] && [ $1 -eq $2 ]
                    echo $?
             

3. 结构性语句(分支语句 / 循环语句)
        a. 分支(条件)语句  
            相当于C语言中的if语句,不可以使用else if

            注意:if后面的条件成立则执行if后面的语句(表达式返回0表示成立,和C语言不一样)
            语法: 
                if  command; then
                语句列表
                fi    #表示if语句结束
                --------------------------
                if  command; then
                语句列表1
                else  #可以不要
                语句列表2
                fi    #表示if语句结束

            如:
                read var1
                if [ `expr $var1 % 2` -eq 0 ]
                then
                        echo 偶数!  # <===> echo "偶数!"
                else
                        echo 奇数!
                fi


        b. 多路分支语句(类似于C语言中的switch)
            语法:
                case 变量 in
                模式1)
                ...
                ;;   #类似于C语言中的break,但是在shell中;;不能省略
                模式2)
                ...
                ;;
                模式n)
                ...
                ;;
                esac   #表示case语句结束

#!/bin/bash

read var
case $var in
a)
    echo var is a
;;
b)
    echo var is b
;;
c)
    echo var is c
;;
esac

            case语句还有一个强大的功能在于可以使用"模式/规则"进行匹配而不是固定的字符串来匹配
            一个模式是由一串正常的字符串和特殊的通配符组成的字符串。该模式可以使用正则表达式(有限支持)
                  *    shell通配符,在shell中表示任意多个任意字符(可以是0个)
                  ?  shell通配符,在shell中表示任意一个字符

输入一个文件名,判断这个文件是什么类型的?

        脚本文件

        头文件

        c语言文件

        C++文件

#!/bin/bash

filename=$1

case $filename in 
*.sh)
    echo "脚本文件"
;;
*.h)
    echo "头文件"
;;
*.c)
    echo "c语言文件"
;;
*.cpp)
    echo "C++文件"
;;
esac

        c. 循环语句   
             for循环
             语法:
                 for 变量名 in 单词表(列表)
                 do 
                     循环体语句
                 done
                
                 单词表: 以空格隔开的字符串列表
                 如:
                      ls输出的结果
                      a b c d e f g h 
                      shell中的数组
                     ....
                 for循环的循环次数就是"单词表"中的单词的个数,并且每次执行的时候,变量的值就是取单词表中的一个单词

for var in a b c d e f g 
do 
    echo $var 
    echo -------
done 


for var in `ls`
do 
echo $var 
echo -------
done 

                for也可以写成C语言的风格
                如: 计算1~100的和

#!/bin/bash
sum=0
for((i=0;i<=100;i++))   #C语言风格的for循环
do 
    sum=`expr $sum + $i`
done 
echo sum=$sum

sum=0
for ((i = 0; i <= 100; i++)) #C语言风格的for循环
do
    sum=`expr $sum + $i`
done
echo sum=$sum

           while循环
                语法:
                      while 命令或者表达式
                      do 
                              循环体语句 
                      done 
                
                命令或者表达式经常是test取测试一个条件

sum=0
i=0
while [ $i -le 100 ]
do
    sum=`expr $sum + $i`
    i=`expr $i + 1`
done
echo sum=$sum
sum=0
i=0
while ((i <= 100))
do
    sum=`expr $sum + $i`
    i=`expr $i + 1`
done
echo sum=$sum

练习:

1. 使用shell,求出1000以内所有的水仙花数


#!/bin/bash

for ((i = 100; i < 1000; i++)) #C语言风格的for循环
do
    a=`expr $i % 10`
    b=`expr $i / 10 % 10`
    c=`expr $i / 100`
    sum=`expr $a \* $a \* $a + $b \* $b \* $b + $c \* $c \* $c`
    if [ $sum -eq $i ]
    then
        echo $i
    fi
done

2. 写一个脚本,统计一个指定文件夹中有多少个普通文件?有多少个目录文件?

#!/bin/bash  

# $#表示命令行参数的个数(不包括脚本的名称)
# -ne  not equal  测试两个整数是否不相等,不相等返回0
if [ $# -ne 1 ]
then 
    echo "arg num error"
fi

file=0
dir=0

cd $1  #把工作路径切换为指定路径
# pwd=`pwd`
for var in `ls -a $1` #列举指定目录中的文件(得到的仅仅是名字列表,不带路径)
do 
    # echo $pwd/$var
    if [ -f $var ]   #判断工作路径下是否有这个普通文件
    then
        file=`expr $file + 1`
    fi

    if [ -d $var ]
    then
        dir=`expr $dir + 1`
    fi

done 

cd -  #把工作路径切换回来

echo file=$file 
echo dir=$dir 

3. 写一个脚本,计算一个指定的文件有多少行?

        read 可以一次读一行

#!/bin/bash 

if [ $# -ne 1 ]
then 
    echo "arg num error"
fi

line=0
while read var  #读取一行
do
    line=`expr $line +  1`
    echo $var 
done   < $1  #整个while内部的输入都来自于$1

#写法叫做代码块重定向,也就是把一组命令同时重定向到一个文件

echo "line = $line" 

4. 写一个简单的脚本,实现文件拷贝的效果(不使用cp命令)

        把第一个文件的每一行内容读出来,写入另一个文件

        read:读

        echo   重定向

#!/bin/bash 

if [ -f $2 ]; then
    rm -rf $2
fi

while read var
do
    echo $var >> $2
done < $1

        until 循环

                语法:
                      until   条件(命令/表达式)
                      do 
                              循环体语句
                      done 

       until和while语句的功能类似,所不同的是,while只有当测试条件满足的时候,进入循环,而until只有当测试条件不满足的时候,才进入循环,条件满足则退出循环,这一点刚好和while相反

#!/bin/bash

sum=0
i=0
until [ $i -gt 100 ]
do
    sum=`expr $sum + $i`
    i=`expr $i + 1`
done
echo until_sum=$sum


sum=0
i=0
until ((i > 100))
do
    sum=`expr $sum + $i`
    i=`expr $i + 1`
done
echo until2_sum=$sum

        d. break和continue 
            break n 
                跳出第n层循环(c语言中只能跳出当前循环)
            continue n 
                转到第n层循环语句开始执行下一次循环
                数字n表示,第几层循环


            break和continue后面也可以不加n,不加n表示的含义和C语言一样

#!/bin/bash

for ((i = 0; i < 10; i++)) # 10次
do
    for ((j = 0; j < 10; j++)) # 10次
    do
        echo $i $j
        continue 2
    done
    echo "nihao"
done

echo hello

#执行结果
0 0
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 0
hello
#!/bin/bash

for ((i = 0; i < 10; i++)) # 10次
do
    for ((j = 0; j < 10; j++)) # 10次
    do
        echo $i $j
        break 2
    done
    echo "nihao"
done

echo hello

#执行结果
0 0
hello

五、shell中的函数

自定义函数的语法:

function_name() {
        函数内部的命令列表
}

function_name: 你自定义的函数的函数名,名字的取法和C语言类似(标识符)
函数不要写参数,如果有参数就是使用"位置变量"

函数定义之后不会自动执行,需要调用才会执行
    
函数的调用语法:
    function_name  arg1 arg2 arg3 ....
    在函数的内部
        arg1 ------> $1
        arg2 ------> $2
        ....

函数的返回值的获取,通常有两种方式:
1. 通过反撇号获取命令/函数的执行结果
    ret=`function_name arg1 arg2`
    反撇号是获取函数function_name执行时输出到标准输出的内容
    把原本输出到终端的所有的输出都赋值给变量ret

#!/bin/bash

sum() {
    s=`expr $1 + $2`
    echo $s
    echo nihao
}

echo start
ret=`sum 123 456`
echo stop
echo $ret

#执行结果
start
stop
579 nihao

2. 使用位置变量 $?
    $?表示的是上一条命令或者程序的退出码(函数的返回值)

    sum 123 132
    结果是sum最后一个命令或者表达式的值
    (在函数里面使用return返回你要返回的值)数字不能超过255

#!/bin/bash

sum() {
    s=`expr $1 + $2`
    echo $s
    return $s
}

echo start
sum 123 132
echo $?
echo stop

#执行结果
start
255
255
stop

六、练习

1. 写一个函数,判断一个通过命令行传入的整数是否为质数

#!/bin/bash 

a=`expr $1 / 2`

for ((i = 2; i <= $a; i++))
do
    num=`expr $1 % $i`
    if [ $num -eq 0 ]
    then
        break
    fi
done

if [ $i -gt $a ]
then
    echo YES
else
    echo NO
fi

2. 写一个shell脚本,打印n以内的所有质数(n通过命令行传入)

#!/bin/bash 

for ((i = 2; i <= $1; i++))
do
    a=`expr $i / 2`
    for ((j = 2; j <= $a; j++))
    do
        num=`expr $i % $j`
        if [ $num -eq 0 ]
        then
            break
        fi
    done
    if [ $j -gt $a ]
    then
        echo $i
    fi
done
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值