Shell编程

1、确定用户用什么shell

  • echo $SHELL 查看环境变量
  • 看passwd文件用户的启动命令 passwd文件路径/etc/passwd
  • 修改用户的shell
    • chsh

2、shell的内建命令
shell的内建命令是不会启动一个进程,而是就在当前shell的进程下去调用一个普通函数。shell的内建命令可以通过help去查询
3、编写一个简单的shell脚本test.sh

#!/bin/bash
echo HelloWorld
cd ..
ls

shell脚本中用#表示注释,相当于c语言的//注释。但如果#位于第一行开头,并且是#!则例外,它表示该脚本使用后面指定的解释器/bin/bash解释执行。如果把这个脚本文件加上可执行权限然后执行

[root@node02 shell_study]# chmod a+x test.sh 
[root@node02 shell_study]# ./test.sh 

4、变量
声明即赋值
varname=value
注意等号两边不能留空格,留了空格就变成了一个命令+两个参数

使用变量
$varname
${varname}
使用花括号来限定变量名的范围

5、变量的分类
shell 内变量

	在shell的解析环境中存在的变量
	
	全局范围的变量(全局变量)
		shell中不使用任何修饰符修饰的变量都是全局变量
		不管是在函数内还是函数外都一样
		从声明语句调用开始一直到脚本结束    都是其声明周期
	
	局部变量
		用local 修饰
		只能声明在函数内
		从声明语句调用开始一直到函数结束

环境变量

		是操作系统自带的,每一个进程都会有
		当启动一个子进程的时候,环境变量是从父进程拷贝到子进程
		子进程做任何环境变量的修改,不会影响父进程
		环境变量是单向传递
		export  varname=value
		或者
			varname=value
			export  varname

删除变量

	不管是shell内变量还是环境变量,使用unset删除变量
	格式:unset 变量名

示例:
global.sh

#!/bin/bash
globalVar1="hello"
function test()
{
    globalVar2="World"
    local localVar3="玉米"
    echo $localVar3
}
#调用函数
test
echo $globalVar1 $globalVar2 $localVar3
export environVar="this is in environ"
#调用子脚本
./subScript.sh
#验证子进程修改环境变量之后父进程有没有改变
echo $environVar
#删除环境变量
unset globalVar1
echo "globalVar1=" $globalVar1

子脚本subScript.sh

#!/bin/bash
echo "sub script start"
echo $globalVar1
echo "sub script end"

export environVar
echo $environVar
#更改环境变量
export environVar="SSSSSSSS"

6、文件名代换
代换发生在命令执行之前
* 匹配0个或多个任意字符
?匹配一个任意字符
[若干字符] 匹配方括号中任意一个字符的一次出现

代换成功的前提,文件必须存在,如果不存在是代换不了的

7、命令代换
执行某一条命令,将这个命令的标准输出的内容存到某个变量

varname=`cmd arg1 arg2..... `
$(cmd) 跟`cmd` 是一样的

获取当前脚本所在目录
curPath=$(cd `dirname $0`;pwd)

示例:

#!/bin/bash
dateTime=$(date)
#dateTime=`date`
echo "dateTime is " $dateTime

#获取当前脚本所在路径,在这个路径创建一个1.txt
curPath=$(cd `dirname $0`;pwd)
echo $curPath
touch $curPath/1.txt

8、算数代换
使用 $(()),只能用±*/,并且只能用于整数运算

var=45
echo $((var+3))

使用$[]也可以同样进行上述运算

var=88
echo $[var+90]

$[base#n], 其中base表示进制, n按照base进制解释,后面再有运算数,按十进制解释

echo $[8#12 + 2]
12

9、转义
将普通字符转换成特殊字符:\r \n
将特殊字符转换成普通字符:$SHELL

10、引号

单引号
保持字符串的字面值
即使字符串中有特殊符号也会转为普通符号

双引号
跟单引号一样,区别在于,支持变量的扩展
作为一个有经验的程序员,使用变量之前,如果变量是作为一个参数来传递的,应该要习惯性的加双引号,防止变量中有空格

示例:

[root@node02 shell_study]# vat=200
[root@node02 shell_study]# echo $vat
200
[root@node02 shell_study]# echo '$vat'
$vat
[root@node02 shell_study]# echo "$vat"
200

11、shell中如何表示真假
直接使用某条命令的返回状态来表示真假
main 函数的返回值
main函数返回 0 表示真
main函数返回非0 表示假

通过$? 来获取上一条命令的返回状态

[root@node02 shell_study]# $?
bash: 0: 未找到命令...
[root@node02 shell_study]# echo $?
127
[root@node02 shell_study]# echo 111
111
[root@node02 shell_study]# echo $?
0

12、条件测试

test

       [ EXPRESSION ]
       
       ( EXPRESSION )
              EXPRESSION is true

       ! EXPRESSION
              EXPRESSION is false

       EXPRESSION1 -a EXPRESSION2
              both EXPRESSION1 and EXPRESSION2 are true

       EXPRESSION1 -o EXPRESSION2
              either EXPRESSION1 or EXPRESSION2 is true

       -n STRING
              the length of STRING is nonzero

       STRING equivalent to -n STRING

       -z STRING
              the length of STRING is zero

       STRING1 = STRING2
              the strings are equal
       
       STRING1 != STRING2
              the strings are not equal

       INTEGER1 -eq INTEGER2
              INTEGER1 is equal to INTEGER2

       INTEGER1 -ge INTEGER2
              INTEGER1 is greater than or equal to INTEGER2

       INTEGER1 -gt INTEGER2
              INTEGER1 is greater than INTEGER2

       INTEGER1 -le INTEGER2
              INTEGER1 is less than or equal to INTEGER2

       INTEGER1 -lt INTEGER2
              INTEGER1 is less than INTEGER2

       INTEGER1 -ne INTEGER2
              INTEGER1 is not equal to INTEGER2

       FILE1 -ef FILE2
              FILE1 and FILE2 have the same device and inode numbers

       FILE1 -nt FILE2
              FILE1 is newer (modification date) than FILE2

       FILE1 -ot FILE2
              FILE1 is older than FILE2
	-b FILE
              FILE exists and is block special

       -c FILE
              FILE exists and is character special

       -d FILE
              FILE exists and is a directory

       -e FILE
              FILE exists

       -f FILE
              FILE exists and is a regular file

       -g FILE
              FILE exists and is set-group-ID

       -G FILE
              FILE exists and is owned by the effective group ID

       -h FILE
              FILE exists and is a symbolic link (same as -L)

       -k FILE
              FILE exists and has its sticky bit set

       -L FILE
              FILE exists and is a symbolic link (same as -h)
	  -O FILE
              FILE exists and is owned by the effective user ID

       -p FILE
              FILE exists and is a named pipe

       -r FILE
              FILE exists and read permission is granted

       -s FILE
              FILE exists and has a size greater than zero

       -S FILE
              FILE exists and is a socket

       -t FD  file descriptor FD is opened on a terminal

       -u FILE
              FILE exists and its set-user-ID bit is set

       -w FILE
              FILE exists and write permission is granted

       -x FILE
              FILE exists and execute (or search) permission is granted

示例

[root@node02 shell_study]# mkdir test
[root@node02 shell_study]# test -d test   //测试test是否为一个目录
[root@node02 shell_study]# echo $?
0   //是的
[root@node02 shell_study]# test -f test  //测试test是否为一个文件
您在 /var/spool/mail/root 中有新邮件
[root@node02 shell_study]# echo $?
1  //不是

13、分支结构if

if 命令 | 条件测试
then
xxxx
elif 命令2 | 条件测试2 ;then #如果then跟if写在同一行,加分号
xxxx
else #else不加then
xxxx
fi

使用read读取用户输入
示例:

#!/bin/bash
echo "Is it morning? Please yes or no"
#读取用户输入的一个变量
read YES_OR_NO
if [ "$YES_OR_NO" = "yes" ]
then 
    echo "Good morning"
elif [ "$YES_OR_NO" = "no" ] ; then
    echo "Good afternoon"
else
    echo "Not recognized"
fi

执行结果
在这里插入图片描述

#!/bin/bash

if [ -f /bin/bash ]
then 
    echo "/bin/bash is a file"
else
    echo "/bin/bash is not a file"
fi

var=1
var2=2
if [ "$var" -eq 1 ] && [ "$var2" -eq 2 ]
then
    echo "yes"
else
    echo "no"
fi

执行结果
在这里插入图片描述

简单的分支结构判断和执行使用 && 和 ||
make && sudo make install
如果make执行失败,那么是不会走后面的命令
echo xxxx || exit -1
如果前面的命令执行失败,那么就执行后面的命令

14、分支结构case

c语言的switch
switch(表达式)
{
case 值1:
	xxxx
	break;
case 值2:
	xxxx
	break;
default:
	xxxx
	break;
}
shell中的case
case 表达式 in
val1 | pattern1)
	xxxx
	;;
val2 | pattern2)
	xxxx
	;;    
*)         #相当于c语言中的default
	xxxx
	;; #相当于c语言中的break
esac    #将case倒着写

示例:

#!/bin/bash
echo "Is it morning? Please answer yes or no!"
#读取用户输入的一个变量
read YES_OR_NO

case "$YES_OR_NO" in
    [yY][eE][sS])
        echo "Good morning"
        ;;
    [nN][oO])
        echo "Good afternoon"
        ;;
    *)
        echo "Not recognized"
        ;;
esac

15、批次创建文件/目录

touch {1..9}.txt    创建1~9.txt
mkdir {1..9}_dir   创建1~9_dir目录

16、for循环
格式:
for varname in 列表 ; do #do可以单独写一行,如果写在for这一行就要分号

echo $varname
done

控制循环次数
for i in {1…100}
do

done

遍历目录
for i in $(ls)
do

done
示例:

#!/bin/bash

for FRUIT in apple,banan,orangle
do
    echo "I like $FRUIT"
done

#1-100累加
sum=0
for i in {1..100}
do
    sum=$[$sum+$i]
done
echo "$sum"

# 遍历当前目录  是文件就输出xx是一个文件,是目录就输出xx是一个目录
for f in $(ls)
do
    if [ -f "$f" ]
    then
        echo "$f is a regular file"
    elif [ -d "$f" ]
    then
        echo "$f is a directory"
    else
        echo "$f is not recognized"
    fi
done

17、while循环
和c语言差不多
格式:
while [ 条件测试 ] ; do
xxxx
done
示例:密码不对重新输入

#!/bin/bash
echo "Please input your passward"
read PASSWAD
while [ "$PASSWAD" != "123456" ]
do
    echo "Sorry,try agin"
    read PASSWAD
done

示例2:密码不对重新输入,输入次数不能大于5次

#!/bin/bash
echo "Please input your passward"
read PASSWAD
erroCount=1 #错误次数
while [ "$PASSWAD" != "123456" ]
do
    if [ "$erroCount" -le 5 ]
    then
        echo "Sorry, try agin"
        read PASSWAD
        erroCount=$[$erroCount+1]
    else
        echo "Error than 5 times,exit"
        break
    fi
done

18、位置参数以及shift
$0 相当于c语言main函数的argv[0]
$1、$2… 这些称为位置参数,相当于c语言main函数的argv[1],argv[2],…
$# 相当于c语言main函数的argc-1,注意这里的#后面不表示注释
$@ 表示参数列表"$1" “$2”…,例如可以用在for循环中的in后面
$* 表示参数列表"$1" “$2” …,同上
$? 上一条命令的Exit Status
$(两个) 当前进程号

位置参数默认就支持10个,当然$@还是支持n个
可以配合shift来进行参数左移,来操作不定参数

示例:算数加法

#!/bin/bash

sum=0
#使用for循环
#for i in $@
#do
#    sum=$[$sum+$i]
#done
#echo $sum

# 使用shift
while [ -n "$1" ]
do
    sum=$[$sum+$1]
    shift
done
echo $sum

执行结果
在这里插入图片描述
19、输出
echo -n 表示不换行
echo -e 解析转义字符

printf 跟c的printf一样
示例
在这里插入图片描述
20、管道
使用 | 将多个命令拼接在一起
原理,就是将前一个命令的标准输出作为后一个命令的标准输入来重定向,标准错误输出是不会重定向

需求,编写一个简单的管道命令,读取标准输入,将标准输入的数据转为大写,然后输出
upper.c

#include <stdio.h>
#include <ctype.h>
int main()
{
    int ch = getchar();
    while(ch != EOF)
    {
        putchar(toupper(ch));
        ch = getchar();
    }
    return 0;
}

在这里插入图片描述在这里插入图片描述more 命令
将标准输入饿内容进行缓慢向下查看,要人工操作向下
只支持向下走,不支持往回走

less 命令
比more更加完善,支持回滚,也支持类似vim的操作,查找 hjkl等

21、tee命令
将标准输出重新输出,同时保存一份到文件
常用场景:开一个服务,服务一直在刷log,需要实时看到log,但是又想将log存成一个文件
示例:
在这里插入图片描述
22、函数
function 函数名() #小括号里面不需要填参数列表
{
local var=value #局部变量
return 1 #return只能返回整数,不能返回其他类型,返回值作为退出状态来使用
}

function关键字可以省略,小括号也可以省略,但是两个必须保留一个,不然解析器不知道是一个函数

调用函数的方法,就跟普通命令一样
函数名 arg1 arg2…
函数的执行状态看return语句,如果没有return语句,就以函数的最后一条执行的指令的返回状态作为整个函数的退出状态

函数的传参:也是使用$1 $2…来获取函数内的参数

函数支持递归
示例:遍历目录,包括子目录,如果是文件就输出xxx是文件,如果是目录,就输出xxx是目录

#!/bin/bash

function visit()
{
    local dir="$1"
    for f in `ls $1`
    do
        if [ -f "$dir/$f" ]
        then 
            echo "$dir/$f is a file"
        elif [ -d "$dir/$f" ]
        then 
            echo "$dir/$f is a dir"
            visit "$dir/$f"
        else
            echo "$dir/$f is not recognized"
        fi
    done
}

visit .

23、shell脚本调试方法
-n 读一遍脚本中的命令但不执行,用于检查脚本中的语法错误
-v 一边执行脚本,一边将执行过的脚本命令打印到标准错误输出
-x 提供跟踪执行信息,将执行的每一条命令和结果依次打印出来

打开调试的方法

  1. bash -x 脚本.sh
  2. 脚本开头 使用 #!/bin/bash -x
  3. 脚本中显式的使用 set -x 打开 set +x 关闭

24、正则表达式
练习:
1.以S开头结尾的字符串

^S

2.以数字结尾的字符串

[0123456789] 匹配任意数字
[0-9]
\d
$ 匹配字符串结尾
[0-9]$

3.匹配空字符串(没有任何字符)

^$
^表示开头
$表示结尾

4.字符串只包含三个数字

^\d\d\d $
或者
^\d{3}$
{n}花括号括起来一个数字,表示前面的单元重复n次

5.字符串只有三到五个字母

控制最少重复次数和最大重复次数
{m,n} m表示前面单元最小重复次数,n表示前面单元最大重复次数[a-zA-Z] 如果中括号有多个区间,区间之间不能留空格或其他分隔符
^ [a-zA-Z]{3,5}$

6.匹配不是a-z的任意字母

[^a-z] 中括号中第一个字符如果是^ ,表示区间取反
^ [^a-z]$

7.字符串有0到1个数字或者字母或者下划线

{0,1} 表示重复0-1次
? 也可以表示0-1次重复
1?$

^\w?$

8.字符串有一个或者多个空白符号(\t\r\n等)

\s 表示空白字符 包括 \t\n\r…
{1,} 表示重复1-n 跟+号一样
^\s+$

^\s{1,}$

9.字符串有0个或者若干个任意字符(除了\n)

. 代表任意字符, 除了\n
^.{,}$
花括号中两个参数置空表示重复次数任意 0-n
^.*$
*号代表前面的单元重复0-n次
问号? 表示0-1次
加号+ 表示1-n次
星号 * 表示0-n次

10.匹配0或任意多组ABC,比如ABC,ABCABCABC

使用小括号来将多个单元重新组合成一个单元
^(ABC)*$

11.字符串要么是ABC,要么是123

| 表示选择,选择两边的正则匹配一个
^(ABC|123)$ 小括号也可以将选择范围控制在括号内

12.字符串只有一个点号

做转义 还是使用
^.$

13.匹配十进制3位整数

100 -999
^ [1-9][0-9]{2}$
匹配十进制 0-999的数字
分段
一位数 [0-9]
两位数 10-99 [1-9] [0-9]
三位数 [1-9][0-9]{2}
^([0-9] | [1-9][0-9]{1,2})$

14.匹配0-255的整数

匹配ip
分段
一位数 [0-9]
两位数 [1-9][0-9]
三位数100-199 :1[0-9]{2} 200-249: 2[0-4][0-9] 250-255: 25[0_5]
^([0-9] | [1-9][0-9] | 1[0-9]{2}2[0-4][0-9]25[0_5])$

25、sort命令
命令从标准输入中读取数据然后按照字符串内容进行排序
-f 忽略字符大小写
-n 比较数值大小
-t 指定分隔符,默认是空格或tab
-k 指定分割后进行比较字段
-u 重复的行只显示一次
-r 反向排序
-R 打乱排序
将/etc/passwd 根据用户id来排序:sort -t: -k3 -n < /etc/passwd

26、uniq命令
去除重复的行,前提是重复的行连续
-c 显示每行重复的次数
-d 仅显示重复过的行
-u 仅显示不曾重复的行

在这里插入图片描述

27、wc,命令
world count
-l 统计行数
-c 统计字数
-w 统计单词
在这里插入图片描述

28、grep命令
-c 只输出匹配行的计数
-i 不区分大小写
-H 文件名显示
-r 递归遍历目录
-n 显示行号
-s 不显示不存在或无匹配文本的错误信息
-v 显示不包含匹配文本的所有行,这个参数经常用于过滤不想显示的行
-E 使用扩展的正则表达式
-p 使用prel的正则表达式
-F 匹配固定的字符串,而非正则表达式

egrep = grep -E
fgrep = grep -F
rgrep = grep -r

grep默认使用的是基础的正则
我们在24学的是prel正则,这个比较常用
29、find命令
在这里插入图片描述在这里插入图片描述

30、sed命令
ed 主要是用来将数据进行选取、替换、删除、新増的命令。
语法:

[root@localhost ~] # sed [选项] '[动作]' 文件名

选项:

-n:一般 sed 命令会把所有数据都输出到屏幕上。如果加入此选项,则只会把经过 sed 命令处理的行输出到屏幕上;
-e: 允许对输入数据应用多条 sed 命令编辑;
-f 脚本文件名:从 sed 脚本中读入 sed 操作。和 awk 命令的 -f 选项非常类似;
-r:在 sed 中支持扩展正则表达式;
-i:用 sed 的修改结果直接修改读取数据的文件,而不是由屏幕输出动作;
a \:追加,在当前行后添加一行或多行。当添加多行时,除最后一行外,每行末尾需要用“\”代表数据未完结;
c \:行替换,用c后面的字符串替换原数据行。当替换多行时,除最后一行外,每行末尾需用“\”代表数据未完结;
i \:插入,在当前行前插入一行或多行。当插入多行时,除最后一行外,每行末尾需要用“\”代表数据未完结;
d:删除,删除指定的行;
P:打印,输出指定的行;
s:字符串替换,用一个字符串替换另一个字符串。格式为“行范围s/旧字串/新字串/g”(和Vim中的替换格式类似);

行数据操作
查看subandnum.sh 的第五行数据

[root@node02 shell_study]# sed -n '5p' subandnum.sh 
do

-n 和 -p 要一起使用
删除第二行到第四行的数据

[root@node02 shell_study]# sed '2,4d' subandnum.sh 
#!/bin/bash
do
    sum=$[$sum+$1] 
    shift
done
echo $sum

可以使用$代表最后一行
追加和插入行数据

[root@node02 shell_study]# sed '$a hello' subandnum.sh 
#!/bin/bash

sum=0
while [ -n "$1" ]
do
    sum=$[$sum+$1] 
    shift
done
echo $sum
hello

a表示在指定行后追加数据,i表示在指定行前追加数据

[root@node02 shell_study]# sed '$i world' subandnum.sh 
#!/bin/bash

sum=0
while [ -n "$1" ]
do
    sum=$[$sum+$1] 
    shift
done
world
echo $sum

行数据替换

[root@node02 shell_study]# cat subandnum.sh | sed '3c sum=1'
#!/bin/bash

sum=1
while [ -n "$1" ]
do
    sum=$[$sum+$1] 
    shift
done
echo $sum

字符串替换
"c"动作是进行整行替换的,如果仅仅想替换行中的部分数据,就要使用"s"动作了。"s"动作的格式如下:

[root@localhost ~]# sed's/旧字符串/新字符串/g' 文件名
把第三行的0换成2
[root@node02 shell_study]# sed '3s/0/2/g' subandnum.sh 
#!/bin/bash

sum=2
while [ -n "$1" ]
do
    sum=$[$sum+$1] 
    shift
done
echo $sum

31、awk
语法格式:

awk [选项] ‘条件{动作}  条件{动作} ... ...’  文件名

awk内置变量:
在这里插入图片描述

grep 更适合单纯的查找或匹配文本

sed 更适合编辑匹配到的文本

awk 更适合格式化文本,对文本进行较复杂格式处理
32、crontab
linux 系统定时器
需求,每天什么时候去做什么事

/etc/crontab

命令格式:
m h dom mon dow user command

m -> miunte 0-59
h -> hour 0-23
dom -> day of month 1-31
mon -> month 1-12
dow -> day of week 0-7 其中0和7都表示星期天

示例:
先写一个脚本(脚本路径:/home/admin/network/shell_study/sh/test.sh),把日期写到当前路径的date.txt文件中

#!/bin/bash
#获取当前路径
curPath=$(cd `dirname $0`;pwd)
date >> "$curPath/date.txt"

然后使用命令:
crontab -e 写我们的定时脚本

*/1 * * * * /home/admin/network/shell_study/sh/test.sh
# 每隔一分钟执行test.sh脚本

最后我们查看sh目录下会多出一个date.txt文件。证明定时任务成功
注意:
当输入crontab-e命令时,出现
在这里插入图片描述错误时,修改默认编译器即可

[root@node02 admin]# vim ~/.bashrc

在打开的文件最后一行加入export EDITOR=/usr/bin/vim
最后是修改的文件生效

[root@node02 admin]# source ~/.bashrc

  1. 0-9a-zA-Z_ ↩︎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值