还不会用sed命令吗?

之前写的一个脚手架为了方便 Docker 部署写了一个 Dockerfilebuild.sh 的脚本,大概长这样:

Dockerfile 复制代码
FROM openjdk:17

# 设置容器名称和重启策略
ARG CONTAINER_NAME=quick-spring
ARG RESTART="always"

# 暴露端口
EXPOSE 8888

# 挂载本地文件系统到容器中
ADD ./core-0.0.1.jar app.jar

# 设置环境变量
ENV TZ=Asia/Shanghai

# 启动命令
CMD java -jar app.jar
shell 复制代码
docker rm -f quick-spring
docker build . -t quick-spring
docker run -d -p 8888:8888 --name quick-spring quick-spring

每次需要部署的时候只需要把 Dockefilebuild.sh 以及 jar 包上传到服务就可以运行 build.sh 一键部署了。

但是每当我用这个脚手架新开一个项目的时候,总是需要重新替换一下 Dockerfilebuild.sh 中的名字,虽然工作量不大,但是改多了也会烦,而且有时候甚至会忘记修改。

程序员都是爱偷懒的,我也不例外。

于是我便想着把这个操作通用化,写一个 shell 脚本完成所有的步骤,实现真正的一键部署。

期间用到了许多实用的知识点,于是便写了这篇文章记录一下。

Sed命令

首先想到的是通过 pom.xml 来获取项目名以及版本号:

ini 复制代码
project_name = $(cat pom.xml |  sed -n 's/.*<artifactId>\(.*\)<\/artifactId>.*$/\1/p')

先来介绍一下 sed 命令

sed 是一个流式文本编辑器,用于在文本流中进行替换、删除、插入等操作,可以很方便地和正则表达式配合使用。

它的格式是:

arduino 复制代码
sed OPTIONS 'COMMAND' FILE

一个最基本的例子是:

arduino 复制代码
sed 's/hello/world/' file

其中 s 表示替换命令,即本次操作进行的是替换操作,将 hello 替换为 world,然后打印出来,如:

但是默认情况下只会替换每一行的第一个匹配项:

需要加上 g 命令才能替换所有匹配项:

如果我只想要打印发生替换的行,那么就加上 p 命令(打印模板块的行)和 -n 选项(仅显示处理后的结果)即:

回到上面的问题:

arduino 复制代码
sed -n 's/.*<artifactId>\(.*\)<\/artifactId>.*$/\1/p'

现在来看的话就清晰多了,首先,这里的 -np 是为了确保只输出替换后的行。

至于中中间的字符串其实应该这么看:

arduino 复制代码
's/   .*<artifactId>\(.*\)<\/artifactId>.*$/   \1   /p'

.*<artifactId>\(.*\)<\/artifactId>.*$/ 这个表示的就是要被替换的文本的正则表达式,而 \1 则表示第一个捕获组,也就是这里的 \(.*\)

值得注意的是这里捕获组的括号用到了转义字符,这是因为 sed 命令默认使用的是最原始的正则表达式,捕获组的括号必须是 \(\) 而不是 ()

而这里的 \1 则表示第一个捕获组,也就是 \(.*\) 捕获到的这个 .* 内容,也就是如果我们匹配到的项目名是 quick-spring ,那么这里的 \1 则是 quick-spring

所以可想而知这里最后输出的其实就是尖括号中间的字符串。

但是上面的写法会有一个问题:

不难看出所有的 artifactId 都被匹配了。

于是便用了一个取巧的方法,只读取前10行:

bash 复制代码
head -n 10 pom.xml |  sed -n 's/.*<artifactId>\(.*\)<\/artifactId>.*$/\1/p'

虽然不适用所有情况(比如 artifactId 写到后面去了),但是在这个脚手架里面还是可以解决问题的(而且一般 artifactId 都是写在前十行):

然后用占位符来替换掉原来的硬编码:

ini 复制代码
FROM openjdk:17

# 设置容器名称和重启策略
ARG CONTAINER_NAME={{projectName}}
ARG RESTART="always"

# 暴露端口
EXPOSE 8888

# 挂载本地文件系统到容器中
ADD ./core-{{version}}.jar app.jar

# 设置环境变量
ENV TZ=Asia/Shanghai

# 启动命令
CMD java -jar app.jar

接着只需要通过 sed 命令进行正则匹配并替换文本即可:

bash 复制代码
sed -e "s/{{projectName}}/${project_name}/" -e "s/{{version}}/${version}/" Dockerfile.template | cat > Dockerfile

注意到这个 -e 选项可以组合多个命令,将多个命令作用于同一个文本。

接下来就是构建的操作了:

bash 复制代码
docker rm -f "${project_name}"
docker build . -t "${project_name}"
docker run -d -p 8888:8888 --name "${project_name} ${project_name}"

test命令

值得注意的是,为了防止多次重复生成 Dockerfile,脚本还对文本是否存在进行了检测,这里用到了 test 命令 -f!

bash 复制代码
if [ ! -f Dockerfile ]; then
  echo "生成Dockerfile"
  sed -e "s/{{projectName}}/${project_name}/" -e "s/{{version}}/${version}/" Dockerfile.template | cat > Dockerfile
fi

test 命令是用于测试文件或条件的工具,通常用于 shell 脚本中的条件判断。在 Unix/Linux 系统中,test 命令通常由 [ ] 表示,它们可以用于测试各种条件,并返回一个布尔值作为结果。

以下是常见的 test 命令及其功能:

  1. 文件测试

    • e FILE:检查文件是否存在。
    • f FILE:检查文件是否存在且为普通文件。
    • d FILE:检查文件是否存在且为目录。
    • r FILE:检查文件是否存在且可读。
    • w FILE:检查文件是否存在且可写。
    • x FILE:检查文件是否存在且可执行。
  2. 字符串测试

    • z STRING:检查字符串是否为空。
    • n STRING:检查字符串是否非空。
    • STRING1 = STRING2:检查两个字符串是否相等。
    • STRING1 != STRING2:检查两个字符串是否不相等。
  3. 整数比较

    • INTEGER1 -eq INTEGER2:检查两个整数是否相等。
    • INTEGER1 -ne INTEGER2:检查两个整数是否不相等。
    • INTEGER1 -gt INTEGER2:检查 INTEGER1 是否大于 INTEGER2。
    • INTEGER1 -lt INTEGER2:检查 INTEGER1 是否小于 INTEGER2。
    • INTEGER1 -ge INTEGER2:检查 INTEGER1 是否大于或等于 INTEGER2。
    • INTEGER1 -le INTEGER2:检查 INTEGER1 是否小于或等于 INTEGER2。
  4. 逻辑测试

    • ! EXPRESSION:返回 EXPRESSION 的逻辑否定值。
    • EXPRESSION1 -a EXPRESSION2:返回 AND 逻辑运算结果。
    • EXPRESSION1 -o EXPRESSION2:返回 OR 逻辑运算结果

代码

shell 复制代码
echo "解析pom.xml"
pom_xml_head10=$(head -n 10 pom.xml)
project_name=$(echo "$pom_xml_head10" | sed -n 's/.*<artifactId>(.*)</artifactId>.*$/\1/p')
version=$(echo "$pom_xml_head10" | sed -n 's/.*<version>(.*)</version>.*$/\1/p')

echo "项目名:$project_name"
echo "版本号:$version"

if [ ! -f Dockerfile ]; then
  echo "生成Dockerfile"
  sed -e "s/{{projectName}}/${project_name}/" -e "s/{{version}}/${version}/" Dockerfile.template | cat > Dockerfile
fi

echo "删除原有容器"
docker rm -f "${project_name}"

echo "构建镜像"
docker build . -t "${project_name}"

echo "启动容器"
docker run -d -p 8888:8888 --name "${project_name} ${project_name}"
相关推荐
ZFB00012 分钟前
【麒麟桌面系统】V10-SP1 2503 系统知识——救援模式显示异常
linux·kylin
第七序章3 分钟前
【Linux学习笔记】初识Linux —— 理解gcc编译器
linux·运维·服务器·开发语言·人工智能·笔记·学习
迎仔10 分钟前
A-总览:GPU驱动运维系列总览
linux·运维
tiantangzhixia13 分钟前
Master PDF Linux 平台的 5.9.35 版本安装与自用
linux·pdf·master pdf
yyy的学习记录19 分钟前
Ubuntu下urdf模型转换成proto模型
linux·运维·ubuntu
jbtianci23 分钟前
Spring Boot管理用户数据
java·spring boot·后端
礼拜天没时间.24 分钟前
自定义镜像制作——从Dockerfile到镜像
linux·docker·容器·centos·bash
xixingzhe224 分钟前
ubuntu安装gitlab
linux·ubuntu·gitlab
强风79432 分钟前
Linux-传输层协议TCP
linux·网络·tcp/ip
那我掉的头发算什么38 分钟前
【Mybatis】Mybatis-plus使用介绍
服务器·数据库·后端·spring·mybatis