Shell编程(Linux学习笔记④)

138 阅读4分钟

入门

Shell是一个命令行解释器,接收到程序或用户的命令,然后调用操作系统内核。可撰写比较复杂的流程,一个易编写、易调试、灵活性强的编程语言。

# cat /etc/shells(查看支持的shell文件

image.png

Redhat默认bash,ubuntu默认dash。

#ls -l /bin/ | grep bash查看默认的版本

image.png

可以看到很多都基于bash
#注释
#!/bin/sh指定解释器,但是sh指向bash

image.png

[atguigu@hadoop101 shells]$ touch helloworld.sh
[atguigu@hadoop101 shells]$ vim helloworld.sh

在 helloworld.sh 中输入如下内容
#!/bin/bash

echo "helloworld"

脚本的常用执行方式

第一种:采用 bash 或 sh+脚本的相对路径或绝对路径(不用赋予脚本+x 权限)
第二种:采用输入脚本的绝对路径或相对路径执行脚本(必须具有可执行权限+x)
【了解】第三种:在脚本的路径前加上“.”或者 source

#第一种
bash ./helloworld.sh
bash /root/scripts/hello.sh(绝对路径)
bash scripts/hello.sh(相对路径)

#第二种,注意权限。读(w)写(r)可执行(x)
chmod +x scripts/hello.sh(加权限
ll scripts/(查权限
直接
/root/scripts/hello.sh(绝对路径执行)
bash scripts/hello.sh(相对路径执行)
但直接
hello.sh(不能执行,因为默认它是命令)
可以直接
./helloworld.sh(最常用)

第三种
source hello.sh
source
. hello.sh
. /root/scripts/hello.sh

第一种相当于启用了bash进程,属于传参,但是第二种直接让环境执行所以需要权限。第三种.是命令,第二种.是当前目录的意思。

ps -l(查看进程,发现子shell

image.png

第三种与前两种的区别,开子 shell 与不开子 shell 的区别就在于,环境变量的继承关系,如在子 shell 中设置的当前变量,父 shell 是不可见的。

变量

系统变量+用户变量,全局变量+局部变量
常用系统变量:$HOME$PWD$SHELL$USER等。

env | less
printenv | less
printenv USER
echo $USER(当变量
ls $HOME
export my_var(导出,即设为全局变量,不需要$符号
ps -f(查看cmd,-bash,bash
unset a

Shell的变量不是静态的,或者说不细分,不需要声明数据类型。千万不能加空格!千万不能加空格 !千万不能加空格!

image.png

子shell修改不影响父shell
自定义变量一般用小写,环境变量大写

image.png

默认为String,想做数值运算需要$[]或者$(())readonly是只读变量,unset可设置可撤销,常量(只读变量)不可unset
变量名不能以数字开头
$1,执行加参数

image.png

image.png

单引号不会误认为是变量。

image.png

image.png

basename获取本身的名称,不获取路径。

echo $#获取当前参数个数,$*所有参数视为一个整体,$@一个数组,可用for循环遍历。

运算符

expr表达式,expression,

expr 1+2(不对
expr 1 + 2(返回3
expr 5\* 2(需要转义

或者上面讲过的,这样必须加上echo进行返回
echo $[5*2]
echo $((5*2))

条件判断

image.png

[atguigu@hadoop101 shells]$ [ 23 -ge 22 ]
[atguigu@hadoop101 shells]$ echo $?
0

[atguigu@hadoop101 shells]$ [ -w|e helloworld.sh ](权限,文件是否存在
[atguigu@hadoop101 shells]$ echo $?
0

[atguigu@hadoop101 ~]$ [ atguigu ] && echo OK || echo notOK
OK
[atguigu@hadoop101 shells]$ [ ] && echo OK || echo notOK
notOK

流程控制

  • IF 基本语法
if [ 条件判断式 ]
then
        程序
elif [ 条件判断式 ]
then
        程序
else
        程序
fi

实例

#!/bin/bash

if [ $1 -eq 1 ]

then

echo "banzhang zhen shuai"

elif [ $1 -eq 2 ]

then

echo "cls zhen mei"

fi

[atguigu@hadoop101 shells]$ chmod 777 if.sh
[atguigu@hadoop101 shells]$ ./if.sh 1
banzhang zhen shuai
  • CASE 基本语法
case $变量名 in
"值 1")
如果变量的值等于值 1,则执行程序 1
;;
"值 2")
如果变量的值等于值 2,则执行程序 2
;;
…省略其他分支…
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac

实例

[atguigu@hadoop101 shells]$ touch case.sh
[atguigu@hadoop101 shells]$ vim case.sh

!/bin/bash
case $1 in
"1")
echo "banzhang"
;;
"2")
echo "cls"
;;
*)
;;
esac

[atguigu@hadoop101 shells]$ chmod 777 case.sh
[atguigu@hadoop101 shells]$ ./case.sh 1
1
  • for 基本语法1(我们熟悉但不是shell中常用的
for (( 初始值;循环控制条件;变量变化 ))

do

程序

done

实例

[atguigu@hadoop101 shells]$ touch for1.sh
[atguigu@hadoop101 shells]$ vim for1.sh

#!/bin/bash
sum=0
for((i=0;i<=100;i++))
do
sum=$[$sum+$i]
done

echo $sum

[atguigu@hadoop101 shells]$ chmod 777 for1.sh
[atguigu@hadoop101 shells]$ ./for1.sh
5050

基本语法2

for 变量 in 值 1 值 2 值 3…
do
程序
done

实例

[atguigu@hadoop101 shells]$ touch for2.sh
[atguigu@hadoop101 shells]$ vim for2.sh

#!/bin/bash
#打印数字
for i in cls mly wls
do
echo "ban zhang love $i"
done

[atguigu@hadoop101 shells]$ chmod 777 for2.sh
[atguigu@hadoop101 shells]$ ./for2.sh
ban zhang love cls
ban zhang love mly
ban zhang love wls

再来感受一下$@$*

image.png

image.png

  • while

比较推荐let写法

image.png

  • 读取控制台输入 image.png

函数

  • $符
"$1"_log_$(date +%s)

chmod +x cmd_test.sh
./cmd_test.sh atguigu

  • basename 输出相对路径
  • dirname 输出绝对路径
  • 自定义函数 调用前必须声明(因为不是编译型语言)
    $?获取返回值

image.png

返回会限制在0~255中,可以不要return,直接echo $s

image.png

image.png

这样就正常显示了

综合运用

保存结果,需要归档命令。如

需求:实现一个每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/),将目录下所有文件按天归档保存,并将归档日期附加在归档文件名上,放在/root/archive 下。 这里用到了归档命令:tar

#!/bin/bash

#判断参数是否为1
if [ $# -ne 1]
then
        echo"参数数量应为1"
        exit
fi

# 从参数中获取目录名称
if [ -d $1 ]
then
        echo
else
        echo
        echo "目录不存在"
        echo
        exit
fi

DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1); pwd)

# 获取当前日期
DATE=$(date +%y%m%d)

# 定义生成的归档文件名称
FILE=archive_${DIR_NAME}_$DATE.tar.gz
DEST=/root/archive/$FILE

# 开始归档文件
echo "开始归档..."
echo

# 用绝对路径很保险
tar -czf $DEST $DIR_PATH/$DIR_NAME

if[ $? -eq 0 ]
then
        echo
        echo "归档成功"
        echo "归档文件为:$DEST"
        echo
else
        echo "归档有问题"
        echo
fi
 
exit

chmod u+x daily_archive.sh
ll

#先创建目录
mkdir /root/archive
./daily_achive.sh ../scripts

image.png


#先创建目录
mkdir /root/archive
./daily_achive.sh ../scripts

image.png

编写定时脚本

分 时 日 月 年(*就不设定) 凌晨两点执行一个脚本自动归档

image.png

正则入门

文本处理工具

grep,sed,awk

^匹配开头

$匹配结束

* 不单独使用,匹配上一个字符匹配0次或多次

用正则匹配一个手机号,1[3578][0-9]

echo "138123456789" | grep ^1[345678][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$($表示结束
echo "138123456789" | grep -E ^1[345678][0-9]{9}$({}扩展)

文本处理工具

  • cut 剪切
cut -d " " -f 1 cut.txt
cut -d " " -f 1 cut.txt(截取二三列,没有第三列就只显示第二列

cat /etc/passwd | grep bash$
  • awk 文本分析工具,空格为默认分隔符,切片再进行分析处理

提取出一行文本,然后执行后面花括号的一系列命令

awk -选项参数 'pattern/{action}' ...

-F 指定输入文件分隔符

-v赋值一个用户定义变量

ll /usr/bin | grep awk

sudo cp /etc/passwd
                    #正则表达式   分隔符: 提取第7列
# 运用 grep 和 cut
cat /etc/passwd | grep ^root | cut -d ":" -f 7
                              #匹配后的传入{}代码块
# 一个awk直接实现grep 和 cut 的操作
cat /etc/passwd | awk -F ":" '/^root/{print $7}'

# 以,逗号分隔
cat /etc/passwd | awk -F ":" '?^root/{print $1","$6","$7}'

# 只显示第一列和第七列
cat /etc/passwd | awk -F ":" '{print $1","$7}'

# 自增1
cat /etc/passwd | awk -v i=1 -F : '{print $3+i}' 

# 使用awk切割ip

实践

mesg(查看是否开启消息功能
who -T(同上
mesg y(开启

#              必须指定控制台
write atguigu pts/1

ctrl+C退出

# 脚本实现的方式
#!/bin/bash

# 查看user是否在线    -i忽略大小写$1第一个参数即用户 传参
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}')

# -z zero 判断是否为0
if [ -z $login_user ]
then
        echo "$1 不在线!"
        echo "脚本退出.."
        exit
fi
# 查看用户是否开启消息功能                     截取第二列
is_allowed=$(who -T | grep -i -m 1 $1 | awk '{print $2}')

if [ $is_allowed != "+" ]
then
        echo "$1 没有开启消息功能"
        echo "脚本退出.."
        exit
fi

# 确认是否有消息发送
if [ -z $2 ]
then
        echo "没有消息发出"
        echo "脚本退出.."
        exit
fi

# 获取要发送的消息
whole_msg=$(echo $* | cut -d " " -f 2- )

# 获取终端
user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}')

# 写入要发送的信息
echo $whole_msg | write $login_user $user_terminal

if [ $? != 0 ]
then
        echo "发送失败!"
else
        echo "发送成功!"
fi

exit

完结撒花~~~~~~~~~