如何有条件地从文件批量安装 apt 软件包?

如何有条件地从文件批量安装 apt 软件包?

目标

我想使用一个文件安装多个 APT 包。

apt_install_from_file "packages" # assumes 'packages' is a readable file in the $PWD

笔记我知道还有其他方法可以做到这一点,而且我正在重新发明轮子来说话,但我希望这种方法可以让其他人的生活更轻松。

文件结构

packages

# ppa
ppa "deadsnakes/ppa"

# packages
apt "tree"

# deb
deb "fzy" [args: "https://github.com/jhawthorn/fzy/releases/download/0.9/fzy_0.9-1_amd64.deb", "$HOME", "fzy_0.9-1_amd64.deb"]

要求

  1. 忽略以 开头的任何行,#因为这是注释
  2. 如果该行以 开头,ppa则使用引号括起来的文本运行以下命令。例如ppa "deadsnakes/ppa"=> sudo add-apt-repository -y ppa:deadsnakes/ppa &> /dev/null
  3. 如果该行以 开头,apt则使用引号括起来的文本运行以下命令。例如apt "tree"=>sudo apt install --allow-unauthenticated -qqy tree
  4. 如果该行以 开头,deb则抓取引号内包含的文本以及抓取args引号内包含的后面的文本。

当前职能:

apt_install_from_file() {

    declare -r FILE_PATH="$1"

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Install package(s)

    if [ -e "$FILE_PATH" ]; then

        cat < "$FILE_PATH" | while read -r LINE; do
            if [[ "$LINE" == *"#"* || -z "$LINE" ]]; then
                continue
            elif [[ "$LINE" == *"ppa"* || -z "$LINE" ]]; then
                # ppa= Grab text enclosed by quotes following "ppa"
                sudo add-apt-repository -y ppa:"$ppa" &> /dev/null \
                   && sudo apt update
            elif [[ "$LINE" == *"apt"* || -z "$LINE" ]]; then
                package="$LINE" # Grab text enclosed by quotes following "apt"
                sudo apt install --allow-unauthenticated -qqy "$package"
            elif [[ "$LINE" == *"deb"* || -z "$LINE" ]]; then
                #package_name= Grab text enclosed by quotes following "deb"
                #url= Grab text enclosed by quotes following "args"
                #target_path= Grab text enclosed by quotes following "args"
                #file_name= Grab text enclosed by quotes following "args"
                #file_path="$target_path" + "/" + "$file_name"  

                wget $url -O $file_path && \
                   sudo dpkg -i $file_path && sudo apt-get install -f && \
                   sudo rm -rf $file_path && sudo apt autoremove -qqy
            fi
        done

    fi

}

问题

我需要帮助根据我的需求列表填写功能的空白。

答案1

已经有格式了用于使用文件控制 apt。这是一个普通的 shell 脚本。

/bin/sh #!/bin/sh 复制代码

# 说明:使用 sudo 运行我:$ sudo install_script.sh
# 警告:所有错误均已消除或删除。我想我不想知道……

# 电力供应局
添加 apt-repository -y ppa:deadsnakes/ppa &> /dev/null
添加 apt-repository -y ppa:font-manager/staging &> /dev/null
添加 apt-repository -y ppa:fish-shell/release-2 &> /dev/null
添加 apt-repository -y ppa:zanchey/asciinema &> /dev/null

# 软件包
apt install --allow-unauthenticated -qqy 树符号链接

# deb
wget https://github.com/jhawthorn/fzy/releases/download/0.9/fzy_0.9-1_amd64.deb -O /tmp/fzy_0.9-1_amd64.deb
apt-安装/tmp/fzy_0.9-1_amd64.deb-qqy
rm /tmp/fzy_0.9-1_amd64.deb

有很多方法可以使脚本变得更复杂并且更有趣。

还有许多方法可以清理脚本并使其更具可读性和可维护性。

答案2

这里使用的语法对 shell 脚本没有任何帮助,但是如果使用正则表达式,解析该文件会稍微容易一些。Bash 的正则表达式比较会将组保存(...)在数组中BASH_REMATCH,因此您可以一次性测试有效行并解析它。例如:

#! /bin/bash
declare -A regex
regex["comment"]='^#(.*)'
regex["ppa"]='ppa "(.*)"'
regex["apt"]='apt "(.*)"'
regex["deb"]='deb "(.*)" \[args: "(.*)", "(.*)", "(.*)"\]'
while read LINE
do
    if [[ $LINE =~ ${regex[comment]} ]]
    then
        printf "Comment: %s\n" "${BASH_REMATCH[1]}"
        continue
    elif [[ $LINE =~ ${regex[ppa]} ]]
    then
        ppa=${BASH_REMATCH[1]}
        printf "PPA: %s\n" "$ppa"
    elif [[ $LINE =~ ${regex[apt]} ]]
    then
        package=${BASH_REMATCH[1]}
        printf "APT: %s\n" "$package"
    elif [[ $LINE =~ ${regex[deb]} ]]
    then
        package_name=${BASH_REMATCH[1]}
        url=${BASH_REMATCH[2]}
        target_path=${BASH_REMATCH[3]}
        file_name=${BASH_REMATCH[4]}
        file_path="$target_path/$file_name"

        printf "package_name: %s\n" "$package_name"
        printf "url: %s\n" "$url"
        printf "file_path: %s\n" "$file_path"
    fi
done < "$1"

使用您的示例文件:

$ ./foo.sh foo.txt
Comment:  ppa
PPA: deadsnakes/ppa
Comment:  packages
APT: tree
Comment:  deb
package_name: fzy
url: https://github.com/jhawthorn/fzy/releases/download/0.9/fzy_0.9-1_amd64.deb
file_path: $HOME/fzy_0.9-1_amd64.deb

答案3

背景:

经过一些研究和反复试验后,我发现我可以将该工具cut与结合使用echo

例子:

为了实现所需的输出,请参考以下示例。

packages

# packages
apt "tree"

test.sh

apt_install_from_file() {

    declare -r FILE_PATH="$1"

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Install package(s)

    if [ -e "$FILE_PATH" ]; then

        cat < "$FILE_PATH" | while read -r LINE; do

            if [[ "$LINE" == *"#"* || -z "$LINE" ]]; then
                continue
            elif [[ "$LINE" == *"apt"* || -z "$LINE" ]]; then
                package="$(echo $LINE | cut -d \" -f2)"
                echo "$package"
            fi

        done
    fi

}

用法

./test.sh packages

分解:

给定这条线package="$(echo $LINE | cut -d \" -f2)",让我们把它分解一下。

  1. 首先,我们来看看echo该行的内容。
  2. 然后,我们将结果 pip 到 ,根据给定的分隔符cut将 的内容拆分$LINE为多个字段。本例中的分隔符是"。我们必须\在 前面放置",因为必须对引号字符进行转义。
  3. 接下来,我们抓取该分割的第二个字段,它包含tree
  4. $()最后,我们将返回的内容存储到package哪种情况下tree(例如$()=> tree)。

修改功能:

apt_install_from_file() {

    declare -r FILE_PATH="$1"

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Install package(s)

    if [ -e "$FILE_PATH" ]; then

        cat < "$FILE_PATH" | while read -r LINE; do
            if [[ "$LINE" == *"#"* || -z "$LINE" ]]; then
                continue
            elif [[ "$LINE" == *"ppa"* || -z "$LINE" ]]; then
                ppa="$(echo $LINE | cut -d \" -f2)"
                sudo add-apt-repository -y ppa:"$ppa" &> /dev/null \
                   && sudo apt update
            elif [[ "$LINE" == *"apt"* || -z "$LINE" ]]; then
                package="$(echo $LINE | cut -d \" -f2)"
                sudo apt install --allow-unauthenticated -qqy "$package"
            elif [[ "$LINE" == *"deb"* || -z "$LINE" ]]; then
                package_name="$(echo $LINE | cut -d \" -f2)"
                url="$(echo $LINE | cut -d \" -f4)"
                target_path="$(echo $LINE | cut -d \" -f6)"
                file_name="$(echo $LINE | cut -d \" -f8)"
                file_path="$target_path/$file_name"  

                wget $url -O $file_path && \
                   sudo dpkg -i $file_path && sudo apt-get install -f && \
                   sudo rm -rf $file_path && sudo apt autoremove -qqy
            fi
        done

    fi

}

结论:

这可能不是我上面列出的问题的最佳解决方案,而且它确实是一个黑客解决方案,所以如果有更好的解决方案,请不要犹豫,写一个更新的答案!

答案4

最近我刚写了一个工具,系统信息,就是为了这个目的。它是一个简单的工具(独立二进制文件),它从 toml 文件而不是纯文本文件读取。无需处理纯文本文件的格式和 shell 脚本中的自定义逻辑。欢迎提出建议和反馈!

  1. 下载curl -L https://github.com/Benjamin-Tan/sysdep/releases/latest/download/sysdep-$(arch)-unknown-linux-gnu.tar.gz | tar -xz -C ~/.local/bin

  2. 创建一个system_dependencies.toml,文件名可以根据您的喜好修改。

    [dependencies]
    apt = ["libpackage", "libpackage2"]
    
  3. sysdep list列出依赖项,应该显示“libpackage”、“libpackage2”。

  4. sysdep install安装它们

  5. sysdep显示更多选项的帮助页面。

相关内容