还不会用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}"
相关推荐
檀越剑指大厂9 分钟前
【Linux系列】Shell 脚本中的条件判断:`[ ]`与`[[ ]]`的比较
linux·运维·服务器
2301_819287122 小时前
ce第六次作业
linux·运维·服务器·网络
武汉联从信息2 小时前
如何使用linux日志管理工具来管理oracle osb服务器日志文件?
linux·运维·服务器
刘大辉在路上2 小时前
突发!!!GitLab停止为中国大陆、港澳地区提供服务,60天内需迁移账号否则将被删除
git·后端·gitlab·版本管理·源代码管理
Aileen_0v03 小时前
【AI驱动的数据结构:包装类的艺术与科学】
linux·数据结构·人工智能·笔记·网络协议·tcp/ip·whisper
州周3 小时前
Ftp目录整个下载
linux·服务器·数据库
Jackey_Song_Odd3 小时前
Ubuntu 24.04.1 解决部分中文字符(门、径)显示错误的问题
linux·ubuntu
kaixin_learn_qt_ing4 小时前
Linux export命令
linux
余额不足121384 小时前
C语言基础十六:枚举、c语言中文件的读写操作
linux·c语言·算法
冷曦_sole4 小时前
linux-19 根文件系统(一)
linux·运维·服务器