shell的基本语法以及如何学会编写shell脚本

一.shell

1.1 shell

shell的本质是一种命令行解释器,它问用户提供与操作系统交互的界面。

1.命令解释:接受用户输入的命令,并将其解释为操作系统可以理解的指令来执行。

2.脚本执行:可以运行 Shell 脚本,这些脚本包含一系列命令,可实现自动化任务。

3.环境配置:允许用户设置环境变量,来定制工作环境。

1.环境变量就是保存系统启动相关以及系统运行相关的一些值的变量。

linux系统中可以使用命令 env 来查看系统中已有的环境变量

可以使用 echo $环境变量名 来查看环境变量的值

2.常见的环境变量

HOME                 保存当前使用的的用户家目录

PWD                    保存当前所在的路径

OLDPWD            保存上一次所在的路径  

SHELL                 保存当前用户使用的默认的命令解析器

PS1                     保存命令行提示符显示的内容及配色方案

PATH                   保存可执行文件的路径

3.声明环境变量的方式

export 环境变量名=环境变量值                                覆盖写

export 环境变量名=$环境变量的名:新的值           追加写

只对当前终端生效方式:直接在终端执行命令 export 环境变量名=环境变量值,立即生

                                        效,多用于测试。

对当前用户生效方式:将声明环境变量的语句添加到~/.bashrc这个脚本文件中,重新打开

                                    终端或执行命令 source ~/.bashrc 立即生效,常用于实际开发。

对系统中的所有用户生效:将声明环境变量的语句添加到/etc/profile这个脚本文件中,重启

                                           或执行命令 source ~/.bashrc 立即生效。

4.管道和重定向:支持将一个命令的输出作为另一个命令的输入(管道),以及将命令的输入和输出重定向到文件。

管道符的作用将前一个命令的标准输出作为后一个命令的标准输入,符号是 |

例:head -n 100 file | tail -n 10         查看文件file第91-100行的内容

入输出重定向符

以输出重定向符为例: >是覆盖    >>追加

输出重定向符: >  >>

命令(或可执行程序)执行的结果输出到指定的文件。

ls / > test.txt     #会将根目录下的文件列表输出到 test.txt 文件中,如果 test.txt 不

                                存在则创建,如果存在则覆盖其原有内容

echo "hello dog" >> test.txt  #会将 "hello dog" 追加到 test.txt 文件的末尾

输入重定向符:<   <<

让命令(或可执行程序)从指定文件中取得输入数据。

1.2 shell命令

终端上使用的命令

1.3shell脚本

shell脚本的本身是一个 .sh 结尾的文件,里面都是命令的集合以及一些复杂的逻辑。

作用:批量的执行命令。

二. shell的基本用法

2.1 shell的变量

shell中的变量是没有类型,默认是字符串

shell中的变量无需提前定义,直接使用

shell中每条指令结束,结尾无需加分号

shell中的标量也要符合命名规范,与c语言相同,hell中变量名一般采用大写字母

2.2 shell的注释    

    # 单行注释

:<<EOF

        多行

        注释

EOF

这种方式相当于利用了输入重定向符的功能。-如果注释的内容里面有输入的语句 就可能出问题 所以这种方式不建议使用。

2.3 shell的字符串处理

2.3.1 计算字符串长度--strlen

#!/bin/bash
STR="hello world"
len=${#STR}
echo $len  #11

2.3.2 字符串拷贝--strcpy

#!/bin/bsah
STR1="hello world"
STR2="beijing"
STR1=${STR2}
echo $STR1  #beijing 
STR1="123"  #123
#通过直接赋值

2.3.3字符串拼接--strcat

#!/bin/bash 
STR1="hello world"
STR2="beijing"
STR1="${STR1}$STR2"
echo $STR1  #hello worldbeijing

STR1="AA${STR1}BB" #AABhello worldB

2.3.4字符串截取--strstr

常用方式如下

#!/bin/bash 
STR1="www.baidu.com"    #字符串的字符从0开始计数

# 从第4个字符开始截取 截取到字符串结束
STR2=${STR1:4}
echo $STR2  # baidu.com

# 从第4个字符开始截取 截取2个字符
STR2 ${STR1:4:2}
echo $STR2  # ba

# 从倒数第6个字符开始截取 截取到字符串结束
STR2 ${STR1:0-6:2} 
echo $STR2  # du

不常用方式:

#!/bin/bash 
STR1="www.baidu.com.www.baidu.com"

# 从左向右截取第一次出现ba后面的内容
STR2=${STR1#*ba}
echo $STR2  # du.com.www.baidu.com

# 从左向右截取第一次出现ba前面的内容
STR2=${STR1##*ba}
echo $STR2  # www.

# 从右向左截取第一次出现ba前面的内容
STR2=${STR1%ba*}
echo $STR2  # www.baidu.com.www.

# 从右向左截取最后一次出现ba前面的内容
STR2=${STR1%%ba*}
echo $STR2  # www.

2.4 shell的输入输出

2.4.1 输出--echo

类似C语言的printf

echo hello world            # echo输出自带换行符

echo -n hello world        # 不想加换行符,-n 选项可以取消

age=23

name="zhangsan"

echo 我是${name},今年${age}了  # echo输出可以自己组装格式

echo -e "nihao\nbeijing"    #echo输出转义字符\n, 加 -e 选项

2.4.2 输入--read

类似C语言的scanf

# read 允许一行以空格分隔输入多个变量的值,但给某个变量输入带空格的字符串是不允许

# 建议一行只读取一个变量的值

read V1 V2

echo V1=${V1} echo V2=${V2}

read -p "请输入您的姓名:" NAME   # -p "描述信息" ,可以用来提示

echo $NAME

read -t  5 VALUE   # -t 5,可以指定等待输入的时间,最多等待5秒

echo $VALUE   

read -n 5 VALUE  # -n 5,可以指定读取5个字符,如果够5个自动读取内容,不需敲回车

echo $VALUE 

read -s PASSWD # -s 选项可以隐藏输入的回显,一般用于读取密码

echo 你输入的密码是[$PASSWD]

#可以多个选项一起使用,需注意选项和参数要匹配

read -p "请输入密码:" -t 5 -n 6 -s PASSWD

echo 您输入的密码是[$PASSWD]

2.5shell的算数运算

shell本身是用来批量执行命令的,并不是用来做复杂的算数运算的。如果需要做复杂的算数运算,可以用C语言写,在shell中调用C程序来处理。shell的场景会涉及一些简单的算数运算,如 计数变量的自增,需要用到shell中的算数运算的方式。

shell如果不特殊处理,所有的运算都会将变量的值当做字符串处理

shell不支持浮点型运算

shell中做算数运算的方式有4种:

(())        $[]        let        expr

上述四种运算方式,执行的效率是依次递减的。

2.5.1 (())

格式:

((表达式))

((表达式1,表达式2,...,表达式n)) #当有多个表达式时 每个表达式都会参与运算

                                                  # 最后一个表达式的结果 就是整个(())运算符的结果

注意事项:

1.(())里面引用变量值的时候 $ 可加可不加

2.(())里面运算符前后的 空格 可加可不加

3.如果想引用运算的结果 需要在(())前加 $ 如 C=$((A+B))

4.(())可以做较复杂的运算 如 ++ -- 如 for 循环 等

for((i=0;i

do

done

#!/bin/bash 
v1=10
v2=20

RET=$((V1+V2))
echo $RET
echo $((V1+V2))
echo #((V1 + V2))
echo #(($V1+$V2))
echo #(($V1 + $V2))
#结果都是30

#如果自增自减运算时 不能加$ (($V1++))是错误的写法
((v1++))
echo $v1 # 11

# 多个表达式 每个表达式都会参与运算
RET=$((++V1, ++V2))
echo $v1  $v2  $RET  # 12 21 21

# (()) 支持for等写法
for((i=0;i<5;i++))
do
	echo "hello world"
done

三. 控制语句

3.1 if..else语句

3.1.1格式

if [ 表达式1 ]     # 注意 if 和 [ ]之间,[ ] 和 表达式 之间都要加至少一个空格

then

        分支1

elif [ 表达式2 ]

then

        分支2

else                #else分支没有then

        其他分支

fi

3.1.2 判断字符串

在if的表达式中,建议使用双引号把字符串引起来。因为字符串中有空格时,表达式会报错。

-z                判断字符串是否为空

-n                判断字符串是否非空

==或=         判断字符串是否相等

!=                判断字符串是否不相等

\>                判断字符串是否大于

\<                 判断字符串是否小于

3.1.3 字符串按整形方式比较

-gt        大于                (great than)

-lt         小于                 (less than)

-eq        等于                (equal)

-ge        大于等于         (great equal)

-le        小于等于          (less equal)

-ne        不等于             (not equal)

逻辑运算:

-a        逻辑与        (and)

-o        逻辑或        (or)

注意事项:

1.在一个[ ]里面要使用 -a   -o

2.在两个[ ]之间要使用 &&   ||

#输入学生的成绩 [90,100]为A [80,90)为B [70,80)为C [607)为D [0,60)为不及格

#!/bin/bash
read -p "输入学生的成绩:" SCORE
if [ &SCORE -lt 0 -o &SCORE -gt 100 ]
then
    echo "范围不合理"
    exit
fi

if [ $SCORE -ge 90 -a $SCORE -le 100 ]
then
	echo "A"
elif [ $SCORE -ge 80 ] && [ $SCORE -lt 90 ]
then
	echo "B"
elif [ $SCORE -ge 70 -a $SCORE -lt 80 ]
then
	echo "C"
elif [ $SCORE -ge 60 ] && [ $SCORE -lt 70 ]
then
	echo "D"
else
	echo "不及格"
fi
3.1.4 判断文件的类型

-e        判断文件是否存在,存在为真,不存在为假

-s        判断文件是否存在,且判断文件是否为非空

-c        判断文件是否存在,且判断文件是否为字符设备文件

-d        判断文件是否存在,且判断文件是否为目录文件

-f         判断文件是否存在,且判断文件是否为普通文件

-L        判断文件是否存在,且判断文件是否为链接文件

-S        判断文件是否存在,且判断文件是否为套接字文件

-p        判断文件是否存在,且判断文件是否为管道文件

1.判断类型时,先-e判断文件是否存在,再判断其他类型

2.对于判断链接文件,即能匹配链接文件,又能匹配被链接文件的类型。所以先判断是否是链接文件,因为其他文件不会匹配链接文件。

# 输入一个文件,输出这个文件的类型

#!/bim/bsah

read -p "请输入一个文件:" FILE
if [ -e "FILE" ]
than
    ehco "文件存在"
    if [ -L $FILE ]
    than
        echo "链接文件"
    elif [ -d $FILE ]
        echo "目录文件"
    elif [ -f $FILE ]
        echo "普通文件"
    fi:
else
    echo "文件不存在"
fi
3.1.5 判断文件的权限

-r         判断文件是否存在 且判断文件是否有读权限

-w        判断文件是否存在 且判断文件是否有写权限

-x         判断文件是否存在 且判断文件是否有执行权限

3.1.6 判断文件的时间戳

文件的时间戳:文件最后一次被修改的时间

-nt        判断两个文件的时间戳,前面的文件是否比后面的新(new than)

-ot        判断两个文件的时间戳,前面的文件是否比后面的新(old  than)

#!/bin/bash

read -p "请输入两个文件" FILE1 FILE2
if [ $FILE1 -nt $FILE2 ]  
than
    echo "yas" 
else
    echo "no"
fi
3.1.7 判断文件的inode号

-ef        两个文件inode号相同为真,不相同为假

同一组硬链接文件inode号相同(查看inod号 estat 文件名ls -i 文件名)

#!/bin/bash 
read FILE1 FILE2
if [ $FILE1 -ef $FILE2 ]
then
	echo "yes"
else
	echo "no"
fi
 3.2 for循环

3.2.1 类似C语言的for循环

for((i=0;i<5;i++))
do
    循环体
done

3.2.2 shell中特有的for循环

for 变量 in 单词列表
do
    循环体
done

1.单词列表的单词之间用空格分隔

2.如果单词列表是连续的,可以用{start..end},如{1..100}

也可使用`seq 1 10`,如果有间隔可以`seq 1 2 10` 表示取1 3 5 7 9(2为步长)

3.单词列表也可是命令的结果,如`ls` $(ls) ---最常用的用法

4.可省略in和单词列表,单词列表来自于命令行的传参


#!/bin/bash

#批量处理文件
for i in `seq 1 10`
do
    echo file_$i
    #处理文件的命令
done


# 给当前路径下所有文件的所属用户项 添加执行权限
for i in `ls`   # 或者$(ls)
do 
    chmod u+x $i
done

#省略in和单词列表输出文件
for i
do
    echo $i
done

3.3while循环

格式:

while [ 表达式 ]  # 注意 while 和 [] 之间 ,[]和表达式之间都要有空格

do

        循环体

done

 # 表达式的用法和 if..else 语句的表达式用法相同

# 1到100累加求和

#!/bin/bash
sum=0
i=0
while [ $i -le 100 ]
do
    sum=$((sum + i))
    ((i++))
done
echo &sum

shell中一般使用while循环都用作死循环使用

死循环的写法: 比如 使用死循环 每秒打印一次系统时间

方式一

whlie [ 1 ]

do

        date

        sleep 1

done

方式二

while ((1))

do

        date

        sleep 1

done

方式三

while true

do

        date

        sleep 1

done

3.4 case..in语句

格式:

case $变量 in

        选项)

                分支1

                 ;;

        选项)

                分支2

                 ;;

               *)

                其他分支

                 ;;

esac

case..in语句和C语言的 switch..case 语句很像,* 的作用类似于C语言中 default,表示如果前面的分支都不满足 就进 * 分支。* 分支如果不需要 也可以不写。

选项的写法支持通配符,常用写法如下:

aa|bb|cc       通配aa或者bb或者cc

file[abc]        通配filea fileb filec

file[a-f]          通配filea - filef

file[1-4]         通配file1 file2 file3 file4

file[^abc]       通配不包含abc中的任意一个字符 语法允许 但是逻辑是反的 不建议用

#!/bin/bsah

read -p "请输入你的选择[Y/n]" CHOOSE
case $CHOOSE in
    [Yy][Ee][Ss]|[Yy])
        echo "yes"
        ;;
    *)
        echo "no"
        ;;
esac

3.5select..in语句

select..in语句会将单词列表生成一个可供用户选择的列表

用户只能通过编号来选择单词 不能通过输入单词来选择

一般都配合着case..in语句一起使用,可以根据用户的选择不同走不同的分支

格式:

select 变量 in 单词列表

do

        代码块

done

#!/bin/bash 

select i in beijing shanghai guangzhou
do
    echo $i	
done

执行结果:

1) beijing

2) shanghai

3) guangzhou

#?

这个#?是提示符 可以通过修改环境变量PS3来修改  如 export PS3="请输入你的选择>>"

3.6 braek和continue的作用

break            立即结束本层的循环

break n         n是一个数字 表示立即结束n层循环

cotinue         理解结束本层的本次循环

cotinue 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

四. 函数

4.1 定义函数的格式

function 函数名(){ 

        函数体

}

1.shell中的函数是没有返回值类型的

2.shell中的函数是没有参数列表的

3.function是声明函数的关键字可以不写(为了方便阅读代码可以写),但是声明函数的 () 必须写

4.shell中的函数名也需要符合命名规范

5.shell中函数如果不被调用 是不会执行的

4.2 函数的调用

函数定义之后,可多次调用

没有参数:函数名

   有参数: 函数名 参数1 参数2 ... 参数n

#!/bin/bash 

# 函数的定义
user_func1(){
	echo $(($1+$2))
}
# 有参数的函数调用
hqyj_func2 10 20
hqyj_func2 100 200

4.3 函数内部参数

在函数内部可以通过环境变量来使用参数

$0                脚本名

$1-$9           调用函数时传递的第1-第9个参数

${10}            第10个参数

封装函数 给参数指定的路径下的所有文件的 所属用户添加执行权限

#!/bin/bash 
user_add_x(){
	LIST=`ls $1`
	for i in $LIST
	do
		chmod u+x $1/$i
	done
}

#执行时如下
# user_add_x 指定的路径

如果在函数内部想使用命令行的参数,需要调用函数时通过传参传递给函数

$@ $* "$@"       将命令行的参数传递给函数

"$*"                    将命令行的参数作为一个整体传递给函数

例:执行脚本 时 bash xxx.sh hello world beijing nihao

#!/bin/bash 
function user_func1(){
	echo $1 # world
	echo $2 # hello
	echo $3 # nihao
	echo $4 # beijing
}

user_func1 $2 $1 $4 $3

user_func2(){
	echo $1
	echo $2
	echo $3
	echo $4
}
user_func2 $@
user_func2 $*
user_func2 "$@"
#输出  hello 
#      world
#      beijing 
#      nihao

user_func2 "$*"
#输出 hello world beijing nihao 下面空三行
# 这种写法 函数中的 $1 就表示命令行的所有参数
        

4.4 函数的返回值

1. 因为shell中的变量默认就是全局的 所以函数中定义的变量 在调用函数结束后还可以使用。只要变量没有加local关键字修饰 就可以在函数调用后继续访问 可以用这种方式返回结果。

#!/bin/bash 
user_add_1(){
	RET=$(($1+$2))
}

user_add_1 10 20
echo $RET  # 30

2.shell中也可以使用return来返回函数运行的结果 在调用处使用 $? 就表示使用函数返回的结果

但是注意,这种方式只能返回 [0,255] 范围的值

#!/bin/bash 
user_add_2(){
	RET=$(($1+$2))
	return $RET
}
user_add_2 10 20
echo $?  # 30
hqyj_add_2 100 200
echo $?  # 44 只能返回 [0,255] 的结果

3.使用echo的方式返回函数运行的结果 在调用处使用 命令置换符 $() 或者 `` 来处理

但是这种用法 函数内部就没法将信息打印到终端了 函数内部所有的echo都会打印到调用处

#!/bin/bash
user_add_3(){
	RET=$(($1+$2))
	echo $RE   #不会输出到终端

XXX=`hqyj_add_3 10 20` 
echo $XXX  # 30

XXX=$(hqyj_add_3 10000 20000)
echo $XXX  # 30000

shell脚本的综合练习

1.判断当前用户的家目录下是否有 file_dir 和 dir_dir 这两个目录文件

如果有 询问用户是否清空?

如果用户输入的是Y 则清空 (删除后新建)

如果用户输入的是其他字符 则不清空 退出脚本(exit)

如果没有 则新建这两个目录文件

2. 在终端读取用户指定的路径 (最好是绝对路径)

将该目录下的所有普通文件拷贝到 file_dir 中

将该目录下的所有目录文件拷贝到 dir_dir 中

并统计拷贝的普通文件和目录文件的个数

3.拷贝完成后,输出拷贝了多少个普通文件和目录文件

并分别输出拷贝了哪些普通文件和哪些目录文件

#!/bin/bash 
FILE_DIR="$HOME/file_dir"
DIR_DIR="$HOME/dir_dir"
COUNT_FILE=0
COUNT_DIR=0

if[ -e FILE_DIR ]
then

	read -p "目录文件$FILE_DIR,是否清空[Y/n]" CHOOSE
	if [$CHOOSE == "Y" ]
	then
		rm -r $FILE_DIR
	else
		echo "目录未清空 脚本结束"
		exit
	fi 
fi 
mkdir $FILE_DIR

if[ -e DIR_DIR ]
then 
	read -p "目录文件$FILE_DIR,是否清空[Y/n]" CHOOSE
	if [$CHOOSE == "Y" ]
	then
		rm -r $DIR_DIR
	else
		echo "目录未清空 脚本结束"
		exit
	fi 
fi 
mkdir $DIR_DIR

read -p "请输入要进行分类的路径(绝对路径):" MY_PATH
if [ -e $MY_PATH ]
then
	if [ -d $MY_PATH ]
	then
		for i in `$MY_PATH`
		do 
			if [ -f $MY_PATH$i ]
			then
				cp $MY_PATH$i $FILE_DIR 
				((COUNT_FILE++))
			elif [ -d $MY_PATH$i ]
			then
				cp -r $MY_PATH/$i $DIR_DIR
				((COUNT_DIR++))
			fi 
		done
		
		echo "----------------------------------------"
		echo "---------------分类完成-----------------"
		echo "----------------------------------------"
		echo "-----------共计操作了 $COUNT_FILE 个普通文件-------------"
		ls $FILE_DIR
		echo "-----------共计操作了 $COUNT_DIR 个目录文件-------------"
		ls $DIR_DIR
		echo "----------------------------------------"
	else
		echo "路径 $MY_PATH 不是目录文件 脚本退出"
		exit
	fi
else
	echo "路径 $MY_PATH 不存在 脚本退出"
	exit
fi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值