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

相关文章

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

PHP应用中处理限流和API节流的最佳实践

《PHP应用中处理限流和API节流的最佳实践》限流和API节流对于确保Web应用程序的可靠性、安全性和可扩展性至关重要,本文将详细介绍PHP应用中处理限流和API节流的最佳实践,下面就来和小编一起学习... 目录限流的重要性在 php 中实施限流的最佳实践使用集中式存储进行状态管理(如 Redis)采用滑动

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

Java实现远程执行Shell指令

《Java实现远程执行Shell指令》文章介绍使用JSch在SpringBoot项目中实现远程Shell操作,涵盖环境配置、依赖引入及工具类编写,详解分号和双与号执行多指令的区别... 目录软硬件环境说明编写执行Shell指令的工具类总结jsch(Java Secure Channel)是SSH2的一个纯J

ShardingProxy读写分离之原理、配置与实践过程

《ShardingProxy读写分离之原理、配置与实践过程》ShardingProxy是ApacheShardingSphere的数据库中间件,通过三层架构实现读写分离,解决高并发场景下数据库性能瓶... 目录一、ShardingProxy技术定位与读写分离核心价值1.1 技术定位1.2 读写分离核心价值二