将较大的脚本拆分为多个脚本并在主脚本中获取它们是否常见?


23

目前,我正在开发一个更大的Bash脚本(这是我的一个开源项目),并且开始变得一团糟。我将逻辑拆分为函数,在可以的地方使用局部变量,并且只声明了少数全局变量。不过,它变得很难维护。

我考虑过将脚本拆分为多个脚本,然后将其作为主脚本的源(类似于其他语言的导入)。

但是我想知道这是否可行。首先,采购多个脚本可能会严重减慢脚本的执行时间,其次,这会使分发更加困难。

那么,这是一个好方法吗,其他(开源)项目是否也采用相同的方法?


4
我一直在寻找一个非常长的shell脚本,并且需要3500行和125KB,我不想尝试维护它。在连接程序时,shell确实很友好,但是当它尝试进行计算时,它就很难看了。我知道您的大多数代码都可以正常工作,并且移植它的成本很高,但是您将来可能需要考虑其他事项。
msw

1
我遇到了同样的问题。我有一个相当大的bash项目sourceforge.net/projects/duplexpr。当前,每个脚本都是自包含的,但是我正在考虑将所有常用功能移动到一个单独的文件中,并在需要的地方包括它们,从而不必在多个位置进行更新。总的来说,我认为最好只调用每个连续脚本而不是源代码。它使独立运行部件成为可能。然后,您必须将变量作为参数传递或传递到参数文件中(或者,如果不想使用独立变量,则可以将其导出。)
乔,22:

Answers:


13

是的,这是一种常见的做法。例如,在Unix的早期,负责引导系统完成从启动阶段到多用户操作的shell代码是一个文件/etc/rc。如今,引导过程由许多shell脚本控制,并按功能分解,并根据需要从中央位置获取常用功能和变量。Linux发行版,Mac,BSD都在不同程度上采用了这种方法。


2
但是在那种情况下,它更多是为了可重用性。不同的脚本使用通用的Shell函数库。
斯特凡Chazelas

16

壳牌当时是工作的正确工具吗?作为一个遇到过代码增长问题的开发人员,我可以告诉您,不应该考虑重写,而应该考虑将代码分成更适合于您希望扩展应用程序规模的内容-也许是python或ruby甚至是perl ?

Shell是一种实用程序语言-它是一种脚本语言-因此很难将其扩展到这些大小。


这正是我要发布的内容。
zwol

特别是考虑到即使像Solaris这样的较早的操作系统现在也都附带了perl和python等。较早的系统使用Shell脚本的原因之一是,可以保证外壳始终可用,但是不能保证更大的Unices(例如HP-UX和Solaris和AIX)包括其他工具。
蒂姆·肯尼迪

7

如果这使您的维护更容易,则可以同时进行。将其拆分为逻辑部分,以便可以轻松维护,然后编写(例如)Makefile以将其重新组合在一起进行分发您可以编写一些快速脚本,以将功能从包含文件复制到输出文件中,而不是该source行或只是做一些琐碎的这样的(你必须重新tabify这一点,因为make需要标签):

all: myscript

myscript: includes/* body/*
    cat $^ > "$@" || (rm -f "$@"; exit 1)

然后,您有一个“源”版本(用于编辑)和一个“二进制”版本(用于简单安装)。


1
啊哈!其他使用简单猫方法的人:)
克莱顿·斯坦利

4

脚本可以按照您的描述进行分解-几乎可以完成任何事情。我想说,“好的方法”是将大型脚本划分出来,找出可以在一部分中作为单独的进程运行的地方,并通过IPC机制进行通信。

除此之外,对于shell脚本,我会将其打包为单个文件。就像您说的那样,这使分发更加困难:您要么必须知道“库”脚本的位置-shell脚本没有很好的标准-要么依赖用户正确设置其路径。

您可以分发一个安装程序来处理所有这些事情,提取文件,将它们放在适当的位置,告诉用户export PROGRAMDIR=$HOME/lib/PROGRAM在〜/ .bashrc文件中添加类似内容。如果$PROGRAMDIR未设置主程序或不包含您期望的文件,则主程序可能会失败。

我不必担心加载其他脚本的开销。开销实际上只是打开一个文件。文本的处理是相同的,尤其是当它们是函数定义时。


1

不管是不是惯例,我认为采购一套出口产品都不是一个好主意。我发现通过源代码执行代码只会造成混乱,并且限制了重用,因为环境和其他变量设置使源代码高度依赖源代码。

最好将应用程序分解成较小的自包含脚本,然后将其作为一系列命令来执行。这可以简化调试,因为您可以在交互式外壳中执行每个独立的脚本,在每次命令调用之间检查文件,日志等。您的单个大型应用程序脚本变成了一个更简单的控制脚本,该脚本在完成调试后仅运行一系列命令。

一组较小的自包含脚本确实遇到了难以安装的问题。


1

采购脚本的另一种方法是简单地使用参数调用它们。如果您已经将大部分功能分解为Shell函数,那么您可能已经很接近能够做到这一点了。以下Bash片段允许将脚本中声明的任何函数用作子命令:

if [[ ${1:-} ]] && declare -F | cut -d' ' -f3 | fgrep -qx -- "${1:-}"
then "$@"
else main "$@" # Try the main function if args don't match a declaration.
fi

不这样做的原因source是为了避免环境和选项污染。


0

这是我的示例,说明如何将大型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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.