05-06 周一 Shell工程目录划分和开发最佳实践

2024-05-07 19:04

本文主要是介绍05-06 周一 Shell工程目录划分和开发最佳实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

05-06 周一 Shell工程目录划分和开发最佳实践
时间版本修改人描述
2024年5月6日10:34:13V0.1宋全恒新建文档
2024年5月6日11:07:12V1.0宋全恒完成

简介

 之前楼主曾经完成过一个shell工程的开发,记得当时项目名称叫做campus-shell,主要是用来一键完成多个模块的部署。当时的模块主要包括:

  • 多个java工程
  • python服务
  • AI芯片上的服务

 其实这也是之前积累的经验。主要是在构造这样的工程时,应该如何分配目录以及存放的东西。

shell工程结构

image-20240506104351907

注意事项

相对路径

 由于在shell工程,主要是通过相对路径进行工作的,在执行shell时,要妥善的处理相对路径。如下所示:

yangfei@ubuntu:~/ci-deploy/module$ cat check_prelinimary.sh 
#!/bin/bashcd $(dirname $0)
pwd
config_file=../config/config.json# 验证当前用户具有sudo权限
validate_sudo() {user_id=`id -u`# systemctl must run as sudo# this script is a convenience wrapper around systemctlif [ $user_id -ne 0 ]; thenecho "Must run as sudo"exit 1fi}

 可以看到,在每个module中的shell执行时,最上层都有如下的代码

cd $(dirname $0)
pwd
config_file=../config/config.json

 上述的代码的主要含义,是切换到当前shell所在的工作目录,并在当前的工作目录下定义配置文件的位置,当然是使用相对路径。这样整体就一致了。

配置文件处理

 配置文件也是一个非常重要的点,因为在shell中,不像python处理ini或者json有比较良好的库,因此我们在处理shell的时候,也需要比较复杂的结构,以相同的策略,也可以使用json的配置文件,然后读取时采用相应的库。

 具体可以参考 04-23 周二 shell环境下读取使用jq 读取json文件

 接下来演示一个案例:

配置文件内容

yangfei@ubuntu:~/ci-deploy$ cat config/config.json 
{"repositories": [{"repo_name": "LLMs_Inference","runner_name": "LLMs_Inference_runner","user": "zhangyi","passwd": "123456","url": "https://github.com/CodexDive/LLMs_Inference","token": "BCTEUWMMR7EU7BFLLO76ADTGFHF3M","directory": "actions-ci","tags": ["inference","gpu","self-hosted", "Linux", "X64"]},{"repo_name": "LLMChat","runner_name": "LLMChat_runner","user": "zhangweixing","passwd": "123456","url": "https://github.com/CodexDive/LLMChat","token": "BCTEUWJ76AILP3ZL54L25JDGE5WBS","directory": "actions-ci","tags": ["application","self-hosted", "Linux", "X64"]}]
}

 上述为一个json的配置文件,这个仓库的主要目标是一件完成为团队的仓库配置CI,想要固话这个目录,标签以及必要的信息。如上所示,我们为每个仓库配置了仓库的名称,运行器名称,以及token,和tags,在json中包含了数组结构,字符串结构,是一个相对复杂的结构。

读取配置文件

yangfei@ubuntu:~/ci-deploy$ cat config_runners.sh 
#!/bin/bashuser_id=`id -u`# we want to snapshot the environment of the config user
if [ $user_id -eq 0 -a -z "$RUNNER_ALLOW_RUNASROOT" ]; thenecho "Must not run with sudo"exit 1
fi
cd $(dirname $0)
config_file="config/config.json"jq -c '.repositories[]' "$config_file" | while IFS= read -r repository; douser=$(echo "$repository" | jq -r '.user')passwd=$(echo "$repository" | jq -r '.passwd')ci_dir=$(echo "$repository" | jq -r '.directory')url=$(echo "$repository" | jq -r '.url')token=$(echo "$repository" | jq -r '.token')tags=$(echo "$repository" | jq -r '.tags | join(",")')runner_name=$(echo "$repository" | jq -r '.runner_name')echo "tags: $Tags"work_dir_cmd="cd /home/$user/$ci_dir"cmd="./config.sh --unattended --url $url --token $token --labels "$tags" --name $runner_name"echo "$cmd"utils/expect_exec_cmd.sh "$user" "$passwd" "$work_dir_cmd" "$cmd"

 上述代码演示了使用jq来读取配置文件的过程(一般我们在部署时,需要先配置好),具体含义,就不再赘述了。

 可以看出上述的额代码,主要是读取配置文件之后,然后组装cmd命令,读取了user和passwd命令,并将这些参数以位置参数的形式传递给了脚本expect_exec_cmd.sh

shell脚本调用

 其实在配置文件处理中已经描述了这个过程。

shell调用参数组织

jq -c '.repositories[]' "$config_file" | while IFS= read -r repository; douser=$(echo "$repository" | jq -r '.user')passwd=$(echo "$repository" | jq -r '.passwd')ci_dir=$(echo "$repository" | jq -r '.directory')url=$(echo "$repository" | jq -r '.url')token=$(echo "$repository" | jq -r '.token')tags=$(echo "$repository" | jq -r '.tags | join(",")')runner_name=$(echo "$repository" | jq -r '.runner_name')echo "tags: $Tags"work_dir_cmd="cd /home/$user/$ci_dir"cmd="./config.sh --unattended --url $url --token $token --labels "$tags" --name $runner_name"echo "$cmd"utils/expect_exec_cmd.sh "$user" "$passwd" "$work_dir_cmd" "$cmd"

 通过读取配置文件中一些参数项的内容,我们完成了expect_exec_cmd.sh的脚本调用参数的组织,该脚本需要四个参数

$1: 用户名

$2: 密码

$3: 命令执行目录

$4: 待执行的命令

注,在调用shell文件的时候,依然采用的事相对位置的调用形式,如上述代码采用的是

utils/expect_exec_cmd.sh "$user" "$passwd" "$work_dir_cmd" "$cmd"

 这表明在执行这部分代码时,是位于ci-deploy目录下的,所以执行环境可以正确找到utils/expect_exec_cmd.sh

shell脚本采用expect自动应答的形式执行命令

yangfei@ubuntu:~/ci-deploy$ cat utils/expect_exec_cmd.sh 
#!/usr/bin/expect -f# author: songquanheng
# date: 2024年4月24日16:17:43
# desc: 该脚本用于使用expect判断用户是否存在以及用户名密码是否匹配# 设置超时时间,这里设置为10秒
set timeout 10# 通过命令行参数传递用户名和密码
set username [lindex $argv 0]
set password [lindex $argv 1]
set work_dir_cmd [lindex $argv 2]
set cmd [lindex $argv 3]puts "username $username"
puts "password $password"
puts "word_dir_cmd $work_dir_cmd"
set status_code 42# puts "username: $username"
# puts "password: $password"
# 启动交互式程序
spawn su - $usernameexpect {"Password:" {# 提供密码send "$password\r"}-re "exist" {puts "user $username does not exist"exit 4}
}expect {-re "Authentication failure" {puts "Login failed. password wrong."exit 1 }-re "$username@" {puts "Login success."}timeout { puts "Login failed. Connection timed out."; exit 2}
}
send "$work_dir_cmd\r"
send "$cmd\r"
expect {"Settings Saved" {puts "runner authentication、registration、settings executes successfully"; exit 0}
}exit $status_code

 当时为开发这个ci-deploy工程,上述代码也是失败的主要原因,因为该命令设置了Timeout 10,但是由于配置运行器需要和github进行交互,需要下载许多的网络资源,一因此执行时间不太保证,所以就失败了,这也是这个工程没有继续执行下去的核心原因所在。

 但,这不影响,shell脚本调用的演示。

 上述shell为expect脚本,主要的作用是接收几个参数来确定执行的用户,以及密码,并在指定目录下执行命令的过程。

注: 该脚本也演示了expect脚本设置变量,打印变量,并使用退出码来判断执行结果的演示,也演示了从调用处读取位置参数

总结

 该文件主要是记录了一个shell工程的最佳实践和目录划分,并且给出了在开发这样工程时的三个注意事项:

  1. 相对路径
  2. 配置文件处理
  3. shell脚本调用

 并且行文在演示shell脚本调用时,采用的是expect自动应答,免交互的形式,方便读者学习和使用。

这篇关于05-06 周一 Shell工程目录划分和开发最佳实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/968122

相关文章

JDK21对虚拟线程的几种用法实践指南

《JDK21对虚拟线程的几种用法实践指南》虚拟线程是Java中的一种轻量级线程,由JVM管理,特别适合于I/O密集型任务,:本文主要介绍JDK21对虚拟线程的几种用法,文中通过代码介绍的非常详细,... 目录一、参考官方文档二、什么是虚拟线程三、几种用法1、Thread.ofVirtual().start(

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

基于Python开发Windows自动更新控制工具

《基于Python开发Windows自动更新控制工具》在当今数字化时代,操作系统更新已成为计算机维护的重要组成部分,本文介绍一款基于Python和PyQt5的Windows自动更新控制工具,有需要的可... 目录设计原理与技术实现系统架构概述数学建模工具界面完整代码实现技术深度分析多层级控制理论服务层控制注

解决docker目录内存不足扩容处理方案

《解决docker目录内存不足扩容处理方案》文章介绍了Docker存储目录迁移方法:因系统盘空间不足,需将Docker数据迁移到更大磁盘(如/home/docker),通过修改daemon.json配... 目录1、查看服务器所有磁盘的使用情况2、查看docker镜像和容器存储目录的空间大小3、停止dock

springboot依靠security实现digest认证的实践

《springboot依靠security实现digest认证的实践》HTTP摘要认证通过加密参数(如nonce、response)验证身份,避免明文传输,但存在密码存储风险,相比基本认证更安全,却因... 目录概述参数Demopom.XML依赖Digest1Application.JavaMyPasswo

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

Java 结构化并发Structured Concurrency实践举例

《Java结构化并发StructuredConcurrency实践举例》Java21结构化并发通过作用域和任务句柄统一管理并发生命周期,解决线程泄漏与任务追踪问题,提升代码安全性和可观测性,其核心... 目录一、结构化并发的核心概念与设计目标二、结构化并发的核心组件(一)作用域(Scopes)(二)任务句柄

Java中的Schema校验技术与实践示例详解

《Java中的Schema校验技术与实践示例详解》本主题详细介绍了在Java环境下进行XMLSchema和JSONSchema校验的方法,包括使用JAXP、JAXB以及专门的JSON校验库等技术,本文... 目录1. XML和jsON的Schema校验概念1.1 XML和JSON校验的必要性1.2 Sche