shell脚本编程笔记

10 篇文章 0 订阅

0. Shell学习笔记

shell文件默认以.sh后缀结尾(仅约定但不影响文件类型)

  1. #!/bin/bash 文件首行指定解析器
  2. #表示注释
1. Shell文件执行
  1. 手动执行
    bash demo.sh # 相对路径或绝对路径
    sh demo.sh
    # 部分linux系统中,sh命令是一个指向bash的软连接
    

手动执行shell文件,不要求文件具有可执行权限

  1. 自动执行
    # 为demo.sh文件新增可执行权限
    chmod +x demo.sh
    
    ./demo.sh # 直接执行,相对路径(只能.)或绝对路径
    
  2. 附加内容:
    • source demo.sh 可以使用source命令执行脚本
    • . demo.sh 使用.命令,与上者等价

该执行操作的区别就是,使用“源”命令执行脚本时,是直接在当前的bash会话环境中去执行脚本的内容。

而其他的执行方式是,在当前的bash会话中,新起一个 子bash 环境去执行对应的脚本,执行环境与当前会话是隔离的,可以直接执行bash打开新会话,exit退出子会话。

如部分场景下当前配置的用户配置修改不生效,则可以使用:

source .profile

刷新当前环境的配置信息,即是在当前环境下直接修改环境配置

2. 变量
1. 系统预定义变量
  1. 常用系统变量
    • $HOME$PWD$SHELL$USERPATH
  2. 变量分全局变量和局部变量,顾名思义
  3. 输出环境变量
    • envprintenv 可以打印出当前的全局变量
    • echo $HOMEprintenv HOME 可以直接输出对应环境变量
    • set 输出当前会话的所有变量,包含全局变量和局部变量
2. 自定义变量
  1. 基本语法
    • name=value= 等于号两边不能有空格
    • unset name,撤销变量
    • readonly name=value,声明一个静态只读变量,而且不能unset
    my_var=Hello
    my_var="Hello, world"
    my_var='Hello, world'
    # 以上全部都是局部变量,父shell里面不能访问
    echo $my_var # 打印看看
    export my_var
    # 导出my_var,my_var被提升为全局变量,父shell可访问,且再修改的值不会影响全局变量
    my_var='Hello, Linux'
    export my_var # 父shell依然是Hello, world
    
    ## 注意当前shell定义的局部变量,子shell也不可用访问,如:
    echo "echo $local_var" > demo.sh
    ./demo.sh ## 打印为空,因为会新起子shell,但无法访问父shell的局部变量
    

定义变量时,不支持表达式直接运算,需要使用命令表达式(var=$((1+2))var=$[1+2]

环境变量$PATH

> echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
3. 特殊变量
  1. $0 在shell脚本中,表示脚本执行的第一个参数,一般为执行脚本的名称
  2. $1 在shell脚本中,表示脚本执行的第二个参数,为执行时的输入参数,以此类推$n
    #!/bin/bash
    ## file demo.sh
    
    echo "==$n=="  # "双引号内的$会被解析(变量名)
    echo '==$n=='  # '单引号会被作为纯字面量值使用
    
  3. $# 获取所有输入参数个数
  4. $* 获取命令行中所有(除$0外)的参数(把所有参数作为一个整体值)
  5. $@ 获取所有(除$0外)的参数(作为一个集合,可以遍历参数)
  6. $? 表示上一次执行命令的返回状态,可用于判断命令的执行状态
3. 运算符
  1. expr 可以直接支持运算

    > expr 1 + 2 # 注意使用空格,表示每一项参数(特殊字符如*需要转义\*表示乘号)
    > a=$(expr 5 \* 2)
    > echo $a # a=10
    ## 可以使用反引号`替换计算结果值
    > a=`expr 5 \* 2`
    > echo $a
    
  2. $((运算表达式))$[运算表达式] 可以做表达式运算,并且看可以直接接受返回值

    > $((2*5))
    > 10
    > $((1+2))
    
    > a=$[3 * 4]
    > echo $a
    
    #!/bin/bash
    ## file add.sh
    
    sum=$[$1+$2]
    echo sum=$sum
    
    > ./add.sh 10 20
    sum=30
    
  3. 命令替换:使用反引号 `` 包裹命令,可以直接执行并替换结果

4. 条件判断
  1. 基本语法

    • test condition_expression,是一种断言型测试
    • [ condition_expression ],简化语法,表达式前后必须要有空格
      注意表达式各项需要有空格隔开
    > a=hello
    > test $a = hello
    > echo $? # echo 0 判断成功
    
    > [ $a = Hello ]
    > echo $? # echo 1 判断失败
    
    > [ $a=123 ]
    > echo $? # echo 0 判断成功,因为"$a=123"被作为一个整体判断为真
    
    > [  ] # 空表达式
    > echo $? # echo 1 判断失败
    
    > [ -z $zeroval ] # 判断变量是否为空
    
  2. 比较运算符
    可以进行普通的 =!=,但是对于整数的比较大小的操作,不能使用 < 等操作符,因为已经被占用为重定向操作

    整数比较操作符作用
    -eq等于
    -neq不等于
    -lt小于
    -gt大于
    -le小于等于
    -ge大于等于
    [ 1 -lt 2 ] # 1 < 2
    

    对于字符串的比较,= 表示相等,!= 表示不相等

    对文件进行判断:

    文件判断操作符作用
    -r文件可读权限
    -w文件可写权限
    -x文件可执行权限
    -e目标文件存在
    -f目标文件为文件类型
    -d目标文件为目录类型
    [ -r demo.sh ] # 当前目录的demo.sh文件可读
    
  3. 逻辑与或(短路运算)

    > a=10
    > [ $a -eq 10 ] && echo ture || echo false
    true
    
5. 流程控制
1. if判断
  1. 单分支
    if [ $condition ];then	# ;分号表示同一行内多个串行的命令
    	$dosomething
    fi
    
    if [ $condition ]
    then $dosomething
    fi
    
    #!/bin/bash
    # demo.sh
    
    if [ $1 = world ];then # 如果参数为空,$1为空则会判断报错,需要一个一元运算符
    	echo hello, world
    fi
    
    # 健壮增强
    if [ "$1" = "world" ];then # 使用字符串占位表达式,$1为空时结果为""空字符串,而不是空
    	echo hello, world
    fi
    
  2. 多分支
    if [ $condition ];then
    	$dosomething
    elif [ $condition ];then
      $other
    else
      $default
    fi
    
  3. 多条件判断
    使用短路运算符 &&|| 连接多个判断语句
    if [ 20 -gt 18 ] && [ 30 -lt 35 ]
    then
      echo between 18 and 35 is ok
    fi
    
2. case语句
var="var"
case $var in
"var1")
	echo var=var1
;;	## 两个分号;类似break
"var2")
	echo var=var2
;;
*)	# 通配,类似default
	echo var is unknown
;;
esac
3. for循环
  1. 基本循环

    for ((初始值;循环控制条件;循环递进))
    do
    	$todo
    done
    

    示例:

    #!/bin/bash
    # file calculation.sh
    
    for (( i=1;i <= $1; i++ )) ## 双小括号用法,里面可以按普通表达式判断和运算
    do
    	sum=$[$sum + $i]
    done
    echo $sum
    
  2. in循环

    快速遍历集合:

    for v in v1 v2 ...
    do
      echo v
    done
    
    for n in {1..100}
    do
      echo n	# 输出1~100
    done
    

    回顾 $*$@

    #!/bin/bash
    
    echo '==========$*=========='
    for p in $*	## 此处$*作为一个参数列表整体替换到in后
    do
    	echo $p
    done
    echo '==========$@=========='
    for p in $@	## 此处$@作为一个参数集合用在in后
    do
    	echo $p
    done
    
    #!/bin/bash
    
    echo '==========$*=========='
    for p in "$*"	## 注意字符串解析表达式作用,in后面被作为单个字符串值整体使用
    do
    	echo $p
    done
    echo '==========$@=========='
    for p in "$@"	## 此处出现差别,虽然是解析表达式,但in后仍是一个参数的集合
    do
    	echo $p
    done
    
4. while循环
while [ condition ]
do
	$doloop
done

示例:

#!/bin/bash
# file calculation.sh

n=1
while [ $n -lt $1 ]
do
	sum=$[ $sum + $n ]
	let sum+=n
	n=$[$n+1]
	let n++
done
echo $sum

n=1
while [ $n -lt $1 ]
do
	## 新语法
	let sum+=n
	let n++
done
echo $sum
6. 控制台输入

read (选项) (参数)命令

  1. 选项:

    • -p:指定读取值时的提示符
    • -t:指定读取值时等待的超时时间(秒),如果没有该选项则一直等待
  2. 参数:

    • 变量:指定读取值后的变量名
    #!/bin/bash
    
    read -t 10 -p "请输入参数:" var
    echo "您输入的为:$var"
    
7. 函数

shell函数是一系列指令的集合,可以看作是一个工具,脚本也有类似的作用,类似重量化的函数

1. 系统函数

系统函数是 shell 已经提供的一些函数和工具

  1. basename
    • basename [string / pathname] [suffix]
    • basename或按路径分割获取参数最后一个末尾的字符串,suffix后缀则是指定了路径后缀,获取时会自动去掉后缀
    #! /bin/bash
    #filename demo.sh
    
    logfile="$1"_log_$(date +%s) # 使用$()表达式作命令替换(结果)
    echo 生成文件为:$logfile # 以时间戳为后缀的文件名
    echo 脚本名称为:$(basename $0 .sh)
    echo 第一个参数:$1
    echo 参数数量为:$#
    
    1. 获取时间信息:
    > date # 默认格式日期
    > date +%s	# 返回当前时间戳
    > date "+%Y-%m-%d %H:%M:%S" # 1970-01-01 00:00:00
    
  2. dirname
    • dirname path 从给定的路径中,获取其所在的目录(父目录),即按路径分割截去末尾的字符串,所表示的目录路径。
    > dirname ../a/b/c
    ../a/b
    > dirname /a/b/c/a.txt
    /a/b/c
    > cd /a/b
    > echo $(cd $(dirname c/a.txt);pwd) # 输出了目标绝对路径,但cd命令并不会干扰执行环境
    /a/b/c
    
2. 自定义函数
[function] funcname[()] # []表示可选
{
	args_1=$1; # 使用$n表示函数的参数,不需要声明参数列表
	args_2=$2;
	echo "todo something and return result";
	[return byteint;]	# 可选,表示函数执行结果状态码0~255
}

在调用函数之前,必须先声明函数定义

#! /bin/bash
# filename add.sh

function add()
{
	sum=$[$1 + $2];
	echo "两数之和:"$sum;
}

read -p "输入第一个数:" a
read -p "输入第二个数:" b
add $a $b
echo 加法运算状态:$? # 函数默认 return 0

function add()
{
	sum=$[$1 + $2];
	echo $sum;
}

sum=$(add $a $b) # 截获函数的输出,不会打印到控制台
echo 和的平方:$(($sum*$sum))
8. 正则表达式
  1. 使用 grep 命令
    > echo demo.txt | grep key # 匹配所有包含key的行
    
    > echo demo.txt | grep ^key # 匹配所有以key为开头的行
    > echo demo.txt | grep key$ # 匹配所有以key为结尾的行
    > echo demo.txt | grep -n ^$ # 匹配所有空行,-n显示对应行号
    
    > echo demo.txt | grep key. # .匹配任意一个字符
    > echo demo.txt | grep a* # 匹配a字符任意多次(包含0次)
    
    > echo demo.txt | grep a[1,3,5] # []表示或区间,","逗号可以省略。匹配a1、a3或a5
    > echo demo.txt | grep a[bcd]e
    > echo demo.txt | grep a[0-9a-z]e
    
    # 与其他正则匹配类似,可以组合不同的匹配方式,灵活处理
    > echo demo.txt | grep ^ab?c+.*z$ # 匹配以 1个a + 0或1个b + 至少1个c 开头、z结尾的行
    
    > echo demo.txt | grep '\$' # 匹配$字符,需要转义且使用单引号
    > echo 13512349876 | grep -E ^1[35679][0-9]{9}$
    # 匹配手机号,需要使用-E选项开启扩展正则表达式{}
    
9. 文本处理工具
  1. cut [选项] filename

    • 负责对数据进行裁剪、剪切使用。
    选项参数功能
    -f指定剪切第几列,可以类似正则表达式的或区间[]内的用法
    -d指定分隔符,按给定分隔符分列,默认\t,只能单字符
    -c不按分割符操作,直接指定剪切的字符范围数字
    # in file cut.txt:
    # a--b-c
    # 1--2-3
    # A--B-C
    
    > cut -d " " -f 1,3 cut.txt
    a-b
    1-2
    A-B
    > cut -d " " -f 2- cut.txt
    -b-c
    -2-3
    -B-C
    
  2. awkgawk

    • awk [选项参数] '/pattern/{action} ...' filename
      • pattern:表示 awk 在数据中查找的内容匹配
      • action:在匹配到内容后,执行的一些操作
    选项参数功能
    -F指定输入的分隔符,默认空格
    -v自定义用户变量,可以用于action运算
    > cat /etc/passwd | awk -F ":" '/^root/ {print $7}'
    /bin/bash # root用户指定的shell终端命令
    > cat /etc/passwd | grep ^root | cut -d ":" -f 7
    /bin/bash # 等效用法
    
    > cat /etc/passwd | awk -F ":" '/^root/ {print $1" "$7}'
    root /bin/bash
    > cat /etc/passwd | awk -F ":" 'BEGIN{print "USER SHELL"}print{$1" "$7}END{print "END OF FILE"}'
    USER SHELL
    root /bin/bash
    ... ...
    END OF FILE
    
    # awk 复杂运算
    > cat /etc/passwd | awk -F ":" '{print $3}' # 输出用户uid
    > cat /etc/passwd | awk -F ":" '{print $3+10000}' # 输出用户10uid
    > cat /etc/passwd | awk -F ":" -v i=10000 '{print $3+i}' # 输出用户10uid
    
    • BEGINEND 为内部模式,表示程序处理的开启和结束
    • 使用 -v 可以自定义变量灵活使用
    • awk 还有一些内部变量:
    选项参数功能
    FILENAME处理的文件名
    NR当前已读的行数(行号)
    NF切割后列的个数
    > awk '/^$/{print NR}' cut.txt # 输出文件空行的行号
    
10. 完结撒花(小练习)

Linux自带 mesgwrite 命令,可用于给其他用户发送消息。

  • mesg [y/n] 查看当前是否开启了消息功能:is y/n。传入参数可以控制开启、关闭
  • 使用 who -T 选项也可以查看用户是否开启了消息功能:[+/-] pts/0
#! /bin/bash
# filename notice.sh

# 查看指定用户,-i忽略大小写,-m指定最大匹配次数(只匹配1次)
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}') # 模糊查找目标用户

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

# 从第二个参数开始,获取所有的消息发送列表
msg_list=$(echo $* | cut -d ' ' -f 2-)

# 目标用户的第一个终端
user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}')

#向目标写入消息
echo $msg_list | write $login_user $user_terminal

if [ $? != 0 ];then
	echo 发送失败!
else
	echo 发送成功!
fi
> notice.sh user1 message1 message2
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值