Shell编程

Shell编程

1、Shell解析器

1、Linux提供的shell解析器

# 当然我是在 macos 查的有一些差异
$ cat /etc/shells
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh

也就是说我们都可以用这些命令执行shell文件,例如

$ vim dist.sh
#!/bin/bash
echo "hello world"

$ bash dist.sh
$ csh dist.sh
$ sh dist.sh
$ ./dist.sh # 如果没有 ./ Linux会认为 dist.sh 是一个命令
...
# 都是可以执行的

2、我们每次查看.sh文件时第一行经常是

#!/bin/bash
#!/bin/sh

类似这样的特殊结构,其实就是告诉Linux执行当前shell文件选择的解析器!(指定解析器)

2、Shell变量

登录Linux系统后,系统会启动一个用户shell。每个shell都是由某个shell(父shell)派生的。例如

# 开启子shell(子bash)
$ bash

在这里插入图片描述

可以层层开启子shell,在当前shell定义的变量称为局部变量,在最外层shell定义的变量就是全局变量常见的有系统预定义的变量,对所有shell都可见,而在子shell中定义的变量在父shell中是不可见的。

了解局部变量和全局变量后,我们就能在shell中声明和使用变量,例如

# 1、系统预定义的变量
# 常见的系统变量有
$PATH
$HOME
$PWD
$SHELL
$USER

echo $USER

# 当然我们也可以使用set查看当前shell所有变量
# env 或者是 set 或者是 
set
'!'=0
'#'=0
'$'=55048
'*'=(  )
-=569XZilms
...
# 或者是查找某一变量
env | grep USER

# 2、自定义变量
# 定义变量 注意 = 号前后不能有空格
name1=alex
name2='alex'
name3="alex"
# 效果都是一样的
message='hell world'
# 此时我们自定义的变量是局部变量,其他的shell不能引用它,可以用export关键字把局部变量变成全局变量
#!/bin/bash
username=laizhenghua
export username

#!/bin/bash
# 3、只读变量
readonly name=alex
echo $name

# 4、撤销变量
# 不能撤销只读变量
# unset: name: cannot unset: readonly variable
unset name

age=1
echo $age
unset age
echo $age

# 关于变量注意点:
# 1.变量名称可以由字母、数字、下划线组成,但是不能以数字开头,环境变量建议大写
# 2.等号两侧不能有空格
# 3.在bash中,变量默认类型都是字符串类型无法直接进行数值的运算
# 4.变量的值如果有空格需要使用双引号或者是单引号括起来

# 5、特殊变量
# 主要是捕获和处理命令行输入的参数
# $n n为数字 $0代表该脚本名称 $1-$9代表第一到第九个参数 十以上的参数需要用大括号括起来例如${10}
vim dist.sh
#!/bin/bash
echo "hello $1"

# 执行
./dist.sh alex
hello alex

# $# 获取所有输入参数个数 常用于循序判断参数个数是否正确以及加强脚本的健壮性
#!/bin/bash
echo "number of params: $#"

# $* 代表命令行所有的参数(所有的参数看成一个整体)
# $@ 代表命令行所有的参数($@把每个参数区分对待,通常搭配循环语句)
#!/bin/bash
echo "params: $@"

./dist.sh alex abc
params: alex abc

# $? 最后一次执行的命令返回的状态 如果这个变量值为0证明上一个命令正确执行 如果这个变量的值为非0则证明上一个命令执行不正确

3、shell运算符

shell作为脚本语言不能像高级语言一样直接执行表达式,执行表达式需要借助特殊的运算符才能实现,例如

# $((运算式)) 或者是 $[运算式]

#!/bin/bash
res1=$[1 + 1]
echo $res1

res2=$((1 + 1))
echo $res2

# 扩展:expr命令配合反引号和命令替换也能实现运算符效果
# 1.命令替换
res3=$(expr 1 + 1)
echo $res3

# 2.反引号
res4=`expr 1 + 1`
echo $res4

4、shell条件判断

# 1.基本语法
test condition 
# 或这是使用中括号
[ condition ] # 前后要有空格
# condition 即为判断条件

laizhenghua@laizhenhuadeAir ahst % a=1
laizhenghua@laizhenhuadeAir ahst % test $a = -1
laizhenghua@laizhenhuadeAir ahst % echo $?
1
# 返回1代表命令执行失败,条件判断结果为假

laizhenghua@laizhenhuadeAir ahst % test $a = 1
laizhenghua@laizhenhuadeAir ahst % echo $?
0
# 返回0代表命令执行成功,条件判断结果为真

# 注意:
# 1.判断不等于是用 !=  例如 [$a != 1]
# 2.在Linux中整数之间比较,不是用大于和小于符号而是用以下符号
# -eq 等于(equal) -ne 不等于(not equal)
# -lt 小于(less than) -le 小于等于(less equal)
# -gt 大于(greater than) -ge 大于等于(greater equal)

laizhenghua@192 ~ % [ 2 -lt 3 ]
laizhenghua@192 ~ % echo $?
0
laizhenghua@192 ~ % [ 2 -gt 3 ]
laizhenghua@192 ~ % echo $?    
1

# 2.按照文件权限进行判断
# -r 有读权限(read)
# -w 有写权限(write)
# -x 有执行权限(execute)

laizhenghua@192 code % [ -r dist.sh ]
laizhenghua@192 code % echo $?
0

# 3.按照文件类型进行判断
# -e 文件存在(existence)
# -f 文件存在并且是一个常规的文件(file)
# -d 文件存在并且是一个目录(directory)

laizhenghua@192 code % [ -f dist.sh ]
laizhenghua@192 code % echo $?
0

# 4.扩展 三元运算效果事项
laizhenghua@192 ~ % [ $USER -eq laizhenghua ] && echo ok || echo 'not ok'
not ok

5、shell流程控制

5.1、if判断
# 1.if判断
# 1.1单分支写法
if [ 条件判断式 ]; then
	... # 程序
fi
# 或者是
if [ 条件判断式 ]
then
	... # 程序
fi

# 例如 vim dist.sh
#!/bin/bash
if [ $USER = laizhenghua ]
then
    echo hello $USER
fi

# 1.2多分支写法
if [ 条件判断式 ]
then
	... # 程序
elif [ 条件判断式 ]
then
	... # 程序
else
	... # 程序
fi
#!/bin/bash
if [ $USER = laizhenghua ];then
    echo hello $USER
elif [ $USER = app ]
then 
    echo hello world
fi

# 1.3扩展:条件表达式如何避免出现空值?([: =: unary operator expected)
#!/bin/bash
if [ $1x = ax ];then
    echo ok
fi
# 假设没有拼接上x,命令行没有传入参数就会报错

# 1.4扩展:逻辑与和逻辑或
# 命令行上的应用
# 逻辑与 -a (and)
# 逻辑或 -o (or)
laizhenghua@192 ~ % a=1
laizhenghua@192 ~ % if [ $a -lt 10 -a $a -gt 0 ];then echo ok; fi
ok

# 脚本上的应用
#!/bin/bash
if [ $1 -lt 10 ] && [ $1 -gt 0 ];then 
    echo ok
fi
#!/bin/bash
if [ $1 -lt 10 ] || [ $1 -gt 0 ];then 
    echo ok
fi
5.2、case语句
# 基本语法
case $变量名 in
"值1")
	... # 程序
;;
"值2")
	... # 程序
;;
*)
	... # 程序
;;
esac

# 基本语法其他说明:
# 1.case 行尾必须为单词in 每一个模式匹配必须以右括号)结束
# 2.双分号 ;; 标识命令序列结束相当于java中的break语句
# 3.最后的 *) 表示默认模式相当于java中的default

# 例如
#!/bin/bash
case $1 in
"1") # 双引号可以省略
    echo ok
;;
"2")
    echo not ok
;;
*)
    echo yes
;;
esac
5.3、for循环
# 基本语法1
for ((初始值; 循环控制条件; 变量变化))
do
	... # 程序
done

# 例如 输出整数
#!/bin/bash
for ((i = 0; i < $1; i++))
do
    if [ $[$i % 2] -eq 0 ]; then
        echo $i
    fi
done

# 基本语法2
for 变量 in 值1 值2 值3 ...
do
	... # 程序
done

# 例如 输出命令行传入的参数
#!/bin/bash
for p in $@
do 
    echo $p
done

# 1-100求和
#!/bin/bash
# {1..100} 标识 1-100 序列
sum=0
for i in {1..100}
do 
    sum=$((sum += $i))
done
echo $sum
5.4、while循环
# 基本语法
while [ 条件判断式 ]
do 
	... # 程序
done

# 例如 1-100 求和
#!/bin/bash
sum=0
index=1
while [ $index -le 100 ]
do
    sum=$[sum += $index]
    index=$[index + 1]
done
echo $sum

# 扩展 let 指令可以直接书写其他语言的写法如
#!/bin/bash
sum=0
index=1
while [ $index -le 100 ]
do
    let sum+=index
    let index++
done
echo $sum

6、shell读取控制台输入

# 基本语法
read (选项) (参数)

# 选项 
# 1. -p 指定读取时的提示符
# 2. -t 指定读取时等待的时间(秒)如果-t参数则会一直等待

# 参数
# 1. 变量:指定读取值的变量名

#!/bin/bash
read -t 30 -p "what's your name: " name
echo hello $name

7、shell函数

7.1、系统函数
# 1.basename
# 描述:取路径里的文件名称,会删掉所有前缀包括最后一个"/"字符,然后将字符串显示出来
# 用法:basename [path][suffix]
# 选项:suffix为后缀如果suffix被指定了,basename会将path中的suffix去掉
# 示例:
#!/bin/bash
file_name=$(basename /Users/laizhenghua/dist/password.txt)
echo $file_name
# password.txt

file=$(basename /Users/laizhenghua/dist/password.txt .txt)
echo $file
# password

# 2.date
# 描述:系统日期函数
# 选项:%Y表示年份,%m表示月份,%d表示日期,%H表示小时,%M表示分钟,%S表示秒钟,%Z表示时区。
# 示例:文件名按当天日期命名
#!/bin/bash
# file_name=log_$(date +%Y-%m-%d %H:%M:%S)
file_name=log_$(date +%Y-%m-%d).log
echo $file_name

# 3.dirname
# 描述:从给定的包含绝对路径的文件名中取出文件名(非目录部分)然后返回剩余的路径(目录部分)
# 用法:basename [dirname]
# 示例:
#!/bin/bash
dir=$(dirname /Users/laizhenghua/dist/password.txt)
echo $dir
# /Users/laizhenghua/dist
7.2、自定义函数
# 1.定义语法
[function] function_name[()]
{
	# 函数体
	[return int;]
}
# 自定义函数使用技巧:
# 1、必须在调用前函数之前先声明函数,shell脚本是逐行运行不会像其他语言一样先编译
# 2、函数返回值,只能通过$?系统变量获得。可以显示的加上return语句(返回值只能在0~255之间),如果不加将以最后一条命令的运行结果作为返回值

# 例如:自定义求和函数
#!/bin/bash
# 定义函数
function sum() {
    echo $[$1 + $2]
}

read -p 'enter the first param: ' a
read -p 'enter the second param: ' b

# 调用函数 sum $a $b
s=$(sum $a $b)

echo $s

8、归档文件

实现每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/),将目录下所有文件按天归档保存,并将归档日期附加在文件名上,归档文件放在/home/back下。

#!/bin/bash
# 判断参数个数
if [ $# -ne 1 ];then 
    echo 'excute erro: params length not qual 1 ! '
    exit
fi

# 判断是否是目录
if [ -d $1 ];then
    echo
else
    echo 'excute erro: $1 不是目录'
    exit
fi

NAME=$(basename $1)
echo $NAME
DIR_PATH=$(cd $(dirname $1); pwd)
# 当前日期
DATE=$(date +%Y-%m-%d)
# 归档文件名称
# FILE=/home/back/archive_$DATE.tar.gz
FILE=~/back/archive_$DATE.tar.gz
# 归档目录文件
echo 'archive start ...'
tar -czf $FILE $DIR_PATH/$NAME

if [ $? -eq 0 ]; then
    echo 'archive success'
else 
    echo 'archive fail'
fi

定时执行归档脚本

# 查看当前系统定时任务
crontab -l

# 编辑定时任务
crontab -e
0 2 * * * ~/script/archive.sh
# 0 分
# 2 时(2代表凌晨两点整)
# * 天
# * 月
# * 年

9、shell正则表达式

正则表达式使用单个字符创来描述、匹配一系列符合某个语法规则的的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。在Linux中grep/sed/awk等文本处理工具都支持通过正则表达式进行模式匹配。

# 1.常规匹配
cat password.txt | grep MySQL
# 2.特殊字符 ^ 匹配一行的开头
# 匹配以M开头的文本
cat password.txt | grep ^M 
# 3.特殊字符 $ 匹配一行的结尾
# 匹配以L结尾的文本
cat password.txt | grep L$
# 思考 ^$ 会匹配什么? 空行

# 4.特殊字符 . 匹配任意一个字符(精确字符个数)
# 匹配M**L字符*代表任意字符 例如 MAIL
cat password.txt | grep M..L
# 匹配M***L字符*代表任意字符 例如 MySQL
cat password.txt | grep M...L

# 5.特殊字符 * 不能单独使用和上一个字符连用表示匹配上一个字符0次或多次
# 匹配rt,rot,root
cat password.txt | grep ro*t 
# 匹配MySQL

# 6.字符区间 [] 匹配某个范围内的一个字符
# [6,8] 匹配6或者8
# [0-9] 匹配一个0-9的数字
# [0-9]* 匹配任意长度的数字字符串
# [a-Z] 匹配一个a-Z之间的字符
# [a-Z]* 匹配任意长度的字母字符串
# [a-c,e-f] 匹配a-c或者e-f直接的任意字符

# 7.特殊字符 \ 表示转义不能单独使用,由于所有特殊字符都有其特定匹配模式,当我们想匹配某一特殊字符就需要转义,让其特殊字符表示它本身
# 匹配e^
echo mysql@qwe^123 | grep 'e\^*' 

# 8.扩展
# a要出现两次
a{2} 
# 特殊字符 + 匹配上一个字符1次或多次
# 特殊字符 ? 匹配上一个字符0次或1次

# 9.匹配手机号
cho '19987131172' | grep -E ^1[345789][0-9]{9}$

10、文本处理工具

# 1.cut 从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段输出
# 基本语法:cut [选项参数] filename
# 选项参数说明:
# -f 列号 提取第几列
# -d 分隔符 按照指定分隔符分割列 默认是制表符 "\t"
# -c 按字符进行切割 后加 n 表示取第几列 比如 -c 1
vim dist.txt
java python
mysql oracle
# 截取第一列
cut -d " " -f 1 dist.txt 
# 截取第一列和第二列
cut -d " " -f 1,2 dist.txt

# 选取系统PATH变量值,第2个":"开始后的所有路径(3-代表第3列及以后)
echo $PATH | cut -d ":" -f 3-

# 切割ifconfig后打印本地回旋ip地址
ifconfig lo0 | grep netmask | cut -d " " -f 2

# 2.awk 文本分析工具把文件逐行的读入以空格为默认分隔符将每行切片,切开的部分再进行分析处理
# 基本语法:awk [选项参数] '/pattern1/{action1} /pattern2/{action2}...' filename
# pattern 表示awk在数据中查找的内容就是匹配模式
# action 在找到匹配内容时所执行的一系列命令
# 选项参数说明:
# -F 指定输入文件分隔符
# -v 赋值一个用户定义的变量(每个初始化变量都必须单独跟在一个 -v 选项之后,即 -v var1=value1 -v var2=value2)

vim dist.txt
java python mysql oracle
java python mysql oracle
awk -F " " -v java_path=$JAVA_HOME '/java/{print java_path}' dist.txt

# 逗号分割输出
awk 'BEGIN{print "start"} {gsub(/ /, ","); print $0} END{print "end"}' dist.txt
# awk的用法非常丰富,更多知识可自行学习

# awk内置变量
# FILENAME 文件名
# NR 已读的记录数(行号)
# NF 浏览记录的域的个数(切割后,列的个数)
awk 'BEGIN{print "start"} {print NR} END{print "end"}' dist.txt

11、扩展脚本一览

1、启动jar包

#!/bin/bash
echo '>>>>>>>>>>>>> remove log file start'
rm -rf ./app.log
rm -rf ./log.path_IS_UNDEFINED
rm -rf ./report
rm -rf ./file/*
echo '>>>>>>>>>>>>> remove log file end'
port=8086
echo "app port=$port"
ip=11.51.141.94
echo "app ip=$ip"
package_path=/opt/app/app-report-8086/file
echo "app package_path=$package_path"
nohup java -jar DockingREST.jar --server.port=$port --ytgz.package.path=$package_path --dist.cas.service-url=http://$ip:$port/DockingREST >app.log 2>&1 &
echo '>>>>>>>>>>>>> app start cuccess ...'

2、docker构建镜像+启动容器(运行jar包)

#!/bin/bash
#sys_date=$(date "+%Y-%m-%d %H:%M:%S")
image_name=app-8087:0.1

container_name=${image_name%%:*}

port=${container_name#*-}

echo "image_name=$image_name container_name=$container_name port=$port"
# image_name=app-8087:0.1 container_name=app-8087 port=8087

docker rmi $image_name
docker build -f Dockerfile --build-arg JAR_FILE=DockingREST.jar -t $image_name .

docker run --name $container_name -p $port:$port \
--ulimit nofile=65535:65535 --ulimit nproc=65535:65535 \
--cap-add SYS_TIME \
-d $image_name
#docker exec -it $container_name date -s "$sys_date"

3、docker启动Nginx(目录挂载)

docker run --name nginx -p 80:80 \
-v /opt/app/docker-app/nginx/html:/usr/share/nginx/html \
-v /opt/app/docker-app/nginx/conf:/etc/nginx/conf.d \
-d nginx

4、Dockerfile脚本

# dist service image
# FROM 设置基础镜像java8
FROM openjdk:8-jdk-alpine

MAINTAINER laizhenghua<3299447929@qq.com>

# 切换工作目录
WORKDIR /
RUN mkdir -p /service
# 执行切换目录
RUN cd /service
RUN mkdir logs
VOLUME /tmp
# 传递参数声明(后面build 时 使用 --build-arg 传递) 即微服务打包的jar包名字
ARG JAR_FILE

COPY ${JAR_FILE} /service/app.jar

CMD echo ${JAR_FILE}
# 指定暴露的端口
EXPOSE 9000
# 执行jar
CMD ["nohup", "java", "-jar", "-Dspring.cloud.nacos.discovery.ip=11.51.141.64","-Dspring.cloud.nacos.discovery.port=9000", "/service/app.jar", "&"]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lambda.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值