如何在软件包的首次安装中运行脚本,而不是在更新期间运行?

我最近开始将一些软件打包并发布到Launchpad上。安装和卸载都正常,但是从一个版本升级到下一个版本的问题比较棘手。
问题在于有一些脚本只需要在软件第一次安装时运行。这些脚本用于填充数据库、创建用户等操作。目前这些脚本是在package.postinst的configure部分调用的。然而,这导致它们在升级时也被调用,如图表所示。
有没有办法在.deb包中包含一个维护者脚本,只在软件第一次安装时执行,而不在升级时执行?或者有什么优雅的方法可以在.deb包中包含一些初始设置脚本呢?
5个回答

看看这个来自Debian wiki的图表,了解维护脚本是如何被调用的: Debian维护脚本流程图

如果你沿着左边(“一切顺利”的路径)往下看,你会发现postinst脚本会被调用,并且使用最近配置的版本。这给了你一个机会区分升级和全新安装 - 在升级情况下,你的postinst将会被这样调用:

postinst configure 1.23-0ubuntu1

其中1.23-0ubuntu1是您的软件包之前安装的版本,而对于全新安装,它将被称为

postinst configure

这也允许您处理从特定版本升级时需要执行操作的情况 - 您可以在postinst中检查该版本。
这使得很容易检查脚本是在“安装”还是“升级”上运行。如果$2为空,则表示是安装。所以:
if [ -z "$2" ]; then
  do install stuff
else
  do upgrade stuff
fi

3请注意,如果您删除了软件包(但没有清除它),然后重新安装,额外的参数也会传递进来。 - skyking

使用debian/preinst文件,您可以在安装时执行操作,但不能在升级时执行。
#!/bin/sh
set -e

case "$1" in
    install)
        # do some magic
        ;;

    upgrade|abort-upgrade)
        ;;

    *)
        echo "postinst called with unknown argument \`$1'" >&2
        exit 0
        ;;
esac

#DEBHELPER#

exit 0

尽管如其名,这是在您的软件包安装之前运行的。因此,您可能无法在此处完成所需的操作。大多数软件包只是在postinst的配置阶段测试用户是否已经创建。这里是colord
$ cat  /var/lib/dpkg/info/colord.postinst
#!/bin/sh

set -e

case "$1" in
    configure)

# create colord group if it isn't already there
    if ! getent group colord >/dev/null; then
            addgroup --quiet --system colord
    fi

# create the scanner group if it isn't already there
    if ! getent group scanner >/dev/null; then
        addgroup --quiet --system scanner
    fi

# create colord user if it isn't already there
    if ! getent passwd colord >/dev/null; then
            adduser --system --ingroup colord --home /var/lib/colord colord \
        --gecos "colord colour management daemon"
        # Add colord user to scanner group
        adduser --quiet colord scanner
    fi

# ensure /var/lib/colord has appropriate permissions
    chown -R colord:colord /var/lib/colord

    ;;
esac    



exit 0

你可以尝试使用debian/preinst脚本与postinst脚本结合使用。
在preinst脚本中,检查你的软件包肯定会安装的一个文件。如果存在该文件,则不执行任何操作(因为你的软件包之前已经安装过),否则执行设置步骤。
如果你的设置步骤要求你的软件包已安装(这种情况下上述方法将不起作用,因为preinst在安装之前运行),那么你的preinst脚本可以写入一个文件,例如:/tmp/setupmypkg。你的postinst脚本只需检查该文件是否存在,如果存在则执行以下两个操作:
- 执行初始设置步骤 - 删除/tmp/setupmypkg文件

2是的,这个方法可以行得通,而且我目前正在做类似的事情。但是它看起来还有点不太正规...我希望能找到更本地化的方式来实现。这并不像一个很奇特的要求,对吧? - Jeroen Ooms

我发现,如果你之前已经安装过软件包,然后卸载(但没有完全清除),再尝试重新安装,那么在“postinst configure”脚本中测试$2的方法不起作用。在这种情况下,“postinst configure”脚本仍然会接收到版本参数。
然而,如果你之前安装了软件包,然后将其删除并完全清除,然后再次安装,那么“postinst configure”脚本将不会在$2中接收到版本参数。

我不这么认为,但你可以很容易地修改preinst/postinst脚本来检查软件包是否是第一次安装,并采取标准操作。
可能是这样的,
在preinst中。
if not is_package_istalled():
    export MY_PACKAGE_FIRST_INSTALL

在postinst中,
if MY_PACKAGE_FIRST_INSTALL:
    Do First Install Setup 

编辑

嗯,也许你可以直接在postinst中检查所有这些,因为我认为dpkg在执行postinst之前不会将软件包的状态设置为已安装,但我不确定。所以上面的内容可能会变成:

在postinst中,

if not is_package_istalled():
    Do First Install Setup 

在这里,is_package_installed 可以是你用来检测安装状态的函数。可能类似于 'dpkg --status packagename'。

或者

为什么不简单地检查你想要进行的更改是否已经存在,只有在它们不存在时才继续进行。


我不明白。IS_INSTALLED是从哪里来的? - Jeroen Ooms
这里没有IS_INSTALLED,它只是伪代码,只是一个例子。IS_INSTALLED可以是像'dpkg --status package_name'这样的命令的输出。我的意思是,在preinst中,你可以检查包是否已安装,并设置一个状态变量,然后根据这个状态变量在postinst中采取相应的动作。 - Owais Lone