目前,我正在开发一个更大的Bash脚本(这是我的一个开源项目),并且开始变得一团糟。我将逻辑拆分为函数,在可以的地方使用局部变量,并且只声明了少数全局变量。不过,它变得很难维护。
我考虑过将脚本拆分为多个脚本,然后将其作为主脚本的源(类似于其他语言的导入)。
但是我想知道这是否可行。首先,采购多个脚本可能会严重减慢脚本的执行时间,其次,这会使分发更加困难。
那么,这是一个好方法吗,其他(开源)项目是否也采用相同的方法?
目前,我正在开发一个更大的Bash脚本(这是我的一个开源项目),并且开始变得一团糟。我将逻辑拆分为函数,在可以的地方使用局部变量,并且只声明了少数全局变量。不过,它变得很难维护。
我考虑过将脚本拆分为多个脚本,然后将其作为主脚本的源(类似于其他语言的导入)。
但是我想知道这是否可行。首先,采购多个脚本可能会严重减慢脚本的执行时间,其次,这会使分发更加困难。
那么,这是一个好方法吗,其他(开源)项目是否也采用相同的方法?
Answers:
是的,这是一种常见的做法。例如,在Unix的早期,负责引导系统完成从启动阶段到多用户操作的shell代码是一个文件/etc/rc
。如今,引导过程由许多shell脚本控制,并按功能分解,并根据需要从中央位置获取常用功能和变量。Linux发行版,Mac,BSD都在不同程度上采用了这种方法。
如果这使您的维护更容易,则可以同时进行。将其拆分为逻辑部分,以便可以轻松维护,然后编写(例如)Makefile以将其重新组合在一起进行分发您可以编写一些快速脚本,以将功能从包含文件复制到输出文件中,而不是该source
行或只是做一些琐碎的这样的(你必须重新tabify这一点,因为make
需要标签):
all: myscript
myscript: includes/* body/*
cat $^ > "$@" || (rm -f "$@"; exit 1)
然后,您有一个“源”版本(用于编辑)和一个“二进制”版本(用于简单安装)。
脚本可以按照您的描述进行分解-几乎可以完成任何事情。我想说,“好的方法”是将大型脚本划分出来,找出可以在一部分中作为单独的进程运行的地方,并通过IPC机制进行通信。
除此之外,对于shell脚本,我会将其打包为单个文件。就像您说的那样,这使分发更加困难:您要么必须知道“库”脚本的位置-shell脚本没有很好的标准-要么依赖用户正确设置其路径。
您可以分发一个安装程序来处理所有这些事情,提取文件,将它们放在适当的位置,告诉用户export PROGRAMDIR=$HOME/lib/PROGRAM
在〜/ .bashrc文件中添加类似内容。如果$PROGRAMDIR
未设置主程序或不包含您期望的文件,则主程序可能会失败。
我不必担心加载其他脚本的开销。开销实际上只是打开一个文件。文本的处理是相同的,尤其是当它们是函数定义时。
不管是不是惯例,我认为采购一套出口产品都不是一个好主意。我发现通过源代码执行代码只会造成混乱,并且限制了重用,因为环境和其他变量设置使源代码高度依赖源代码。
最好将应用程序分解成较小的自包含脚本,然后将其作为一系列命令来执行。这可以简化调试,因为您可以在交互式外壳中执行每个独立的脚本,在每次命令调用之间检查文件,日志等。您的单个大型应用程序脚本变成了一个更简单的控制脚本,该脚本在完成调试后仅运行一系列命令。
一组较小的自包含脚本确实遇到了难以安装的问题。
这是我的示例,说明如何将大型bash脚本拆分为多个文件,然后构建为一个结果脚本:https : //github.com/zinovyev/bash-project
我Makefile
为此使用a :
TARGET_FILE = "target.sh"
PRJ_SRC = "${PWD}/src/main.sh"
PRJ_LIB = $(shell ls -d ${PWD}/lib/*) # All files from ./lib
export PRJ_LIB
SHELL := /bin/env bash
all: define_main add_dependencies invoke_main
define_main:
echo -e "#!/usr/bin/env bash\n" > ${TARGET_FILE}
echo -e "function main() {\n" >> ${TARGET_FILE}
cat "${PRJ_SRC}" | sed -e 's/^/ /g' >> ${TARGET_FILE}
echo -e "\n}\n" >> ${TARGET_FILE}
invoke_main:
echo "main \$$@" >> ${TARGET_FILE}
add_dependencies:
for filename in $${PRJ_LIB[*]}; do cat $${filename} >> ${TARGET_FILE}; echo >> ${TARGET_FILE}; done