在新的 Ubuntu Server 20.04.2 LTS 32 位服务器操作系统上安全运行第一个 apt 升级?

在新的 Ubuntu Server 20.04.2 LTS 32 位服务器操作系统上安全运行第一个 apt 升级?

错误

在具有 4GB RAM 的 RaspberryPi 上运行适用于 armhf 架构的新 Ubuntu Server 20.04.2 LTS 32 位服务器操作系统时sudo apt update,我遇到以下错误:sudo apt upgrade

为了sudo apt update

E:http://ports.ubuntu.com.../focal-updates/Inrelease 的发布文件尚未生效(在 113 天 22 小时 43 分钟内无效)。不会应用此存储库的更新。

为了sudo apt upgrade

等待缓存锁定:无法获取锁定 /var/lib/dpkg/lock-frontend。它由进程 3381 (unattended-upgr) 持有。

代码

下面的代码首先检查互联网是否可用,如果不可用,则检查 wifi ssid 和密码是否已添加到配置中,如果没有,则提示用户输入,然后激活这些设置,直到建立互联网连接。然后它执行和sudo apt update命令sudo apt upgrade

# Verify internet access is available
while [ $(ping -q -w1 -c1 google.com &>/dev/null && echo online || echo offline) == offline ]
do

    sleep 1
    # get wifi module
    module=$(ls /sys/class/net)
    module_list=($module)
    wlan_module=${module_list[-1]}
    echo $wlan_module

    # get wifi configuration file
    config_filename=$(ls /etc/netplan)
    echo $config_filename
    config_filepath="/etc/netplan/$config_filename"
    
    # check if the wifi is already added.
    if ! grep -q "$wlan_module" "$config_filepath"; then
        echo "have to add wifi"

        # ask  wifi pwd ssid
        read -p "What is the wifi ssid you want to connect to(Case sensitive)?" ssid

        # ask wifi pwd
        read -p "What is the wifi pwd?" pwd

        # append content
        echo "    wifis:" | sudo tee -a $config_filepath 
        echo "        wlan0:" | sudo tee -a $config_filepath
        echo "            dhcp4: true" | sudo tee -a $config_filepath
        echo "            optional: true" | sudo tee -a $config_filepath
        echo "            access-points:" | sudo tee -a $config_filepath
        echo "                \"$ssid\":" | sudo tee -a $config_filepath
        echo "                    password: \"$pwd\"" | sudo tee -a $config_filepath

        # generate a config file
        sudo netplan generate

        # apply the config file
        sudo netplan apply
        
    fi
done

# set timezone based on ip
export tz=`wget -qO - http://geoip.ubuntu.com/lookup | sed -n -e 's/.*<TimeZone>\(.*\)<\/TimeZone>.*/\1/p'` &&  timedatectl set-timezone $tz
export tz=`timedatectl status| grep Timezone | awk '{print $2}'`

# Verify timezone is set correctly.
timedatectl status

yes | sudo apt update
read -p "Update completed <type enter>" next
yes | sudo apt upgrade
read -p "Upgrade completed <type enter>" next

代码使用以下方式执行:

sudo mkdir /media/usbstick
sudo mount /dev/sda1 /media/usbstick
/media/usbstick/.first_run.sh

时区详细信息

为了回应评论,我添加了自动设置时区的代码,并且手动验证了时区是否设置正确。

假设

我认为这是因为 Ubuntu 服务器正在后台自动执行某种更新/升级,从而阻止我sudo apt upgrade同时运行该命令。

问题

sudo apt upgrade因此,当我手动终止阻止命令的进程时,sudo kill -9 <process_id>我能够成功运行该sudo apt upgrade命令。但是,我可以想象这不是确保升级命令成功完成的最佳方法。所以我目前的方法是编写一个while-loop扫描命令输出sudo apt upgrade直到它产生成功输出的程序。然而,这感觉就像重新发明轮子,我可以想象可能存在更好的方法来解决这个问题。因此,我想问:

如何sudo apt upgrade在新安装的 Ubuntu Server 20.04 LTS 32 位服务器上安全、自动地运行命令,以便我的 Ubuntu Server 后续脚本的其余部分能够正确完成?

答案1

我当前的实现由两个 while 循环组成,它们运行两个sudo apt updatesudo apt upgrade命令,直到验证它们各自的输出。它远非优雅,但似乎有效。增强的first_run.sh脚本包含以下内容:

# Assumes both strings have equal lengths
equal_strings() {
    left=$1
    right=$2
    
    # Initialise the default test result to false
    test_result=false
    
    # Set the test result to true if the left and right string are equal
    if [ "$left" = "$right" ]; then
        echo "Strings are equal."
        test_result=true
    else
            echo "Strings are not equal."
    fi
    
    # Output true or false to pass the equality test result to parent function
    echo $test_result
}


# Assumes the actual result is shorter than the allowed result
right_is_in_tail_of_left() {
    left=$1
    right=$2
    right_size=${#right}
    left_tail_chars=$(echo -n $left | tail -c $right_size)

    # Output true or false to pass test result to parent function
    echo $(equal_strings "$left_tail_chars" "$right")
}


# Makes the main script runnable, removes the log file and runs main file.
actual_result_has_any_allowed_result_in_tail() {
    # declare default function output
    test_result=false
    
    # get the actual result
    actual_result=$1
    shift # Shift all arguments to the left (original $1 gets lost)
    
    # get the list of allowed results and list size
    allowed_results=("$@") 
    list_size=${#allowed_results}

    # Only engage this function if the list size is greater than 1
    if [ $list_size -gt 1 ]; then echo "error";
    
        # if the actual result is in the acceptable list ensure function returns true
        for allowed_result in "${allowed_results[@]}"
        do
            if [ $test_result != true ]; then
                # compute lengths of result strings
                allowed_result_size=${#allowed_result}
                actual_result_size=${#actual_result}
                
                # TODO: remove this if condition and directly go to Else by switching lt to ge
                if [ $actual_result_size -lt $allowed_result_size ]; then echo "error";
                    echo "The actual size is:"
                    echo $actual_result_size
                    echo "WHEREAS allowed_result_size=" 
                    echo $allowed_result_size
                    echo "so actual is smaller than allowed, so do nothing"
                else 
                    # test if left string is in the tail of the allowed result string
                    # TODO: include contains option in separate function
                    temp_test_result=$(right_is_in_tail_of_left "$actual_result" "$allowed_result");
                    if [ $(echo -n $temp_test_result | tail -c 4) == true ]; then
                        test_result=true
                    fi
                fi
            fi
        done
    fi
        
    # Ensure the last 4/5 characters of the output of this function contains the true false evaluation.
    echo $test_result
}




# Verify internet access is available
while [ $(ping -q -w1 -c1 google.com &>/dev/null && echo online || echo offline) == offline ]
do

    sleep 1
    # get wifi module
    module=$(ls /sys/class/net)
    module_list=($module)
    wlan_module=${module_list[-1]}
    echo $wlan_module

    # get wifi configuration file
    config_filename=$(ls /etc/netplan)
    echo $config_filename
    config_filepath="/etc/netplan/$config_filename"
    
    # check if the wifi is already added.
    if ! grep -q "$wlan_module" "$config_filepath"; then
        echo "have to add wifi"

        # ask  wifi pwd ssid
        read -p "What is the wifi ssid you want to connect to(Case sensitive)?" ssid

        # ask wifi pwd
        read -p "What is the wifi pwd?" pwd

        # append content
        echo "    wifis:" | sudo tee -a $config_filepath 
        echo "        wlan0:" | sudo tee -a $config_filepath
        echo "            dhcp4: true" | sudo tee -a $config_filepath
        echo "            optional: true" | sudo tee -a $config_filepath
        echo "            access-points:" | sudo tee -a $config_filepath
        echo "                \"$ssid\":" | sudo tee -a $config_filepath
        echo "                    password: \"$pwd\"" | sudo tee -a $config_filepath

        # generate a config file
        sudo netplan generate

        # apply the config file
        sudo netplan apply
        
    fi
done

# https://askubuntu.com/questions/323131/setting-timezone-from-terminal
echo "Setting TimeZone..."
export tz=`wget -qO - http://geoip.ubuntu.com/lookup | sed -n -e 's/.*<TimeZone>\(.*\)<\/TimeZone>.*/\1/p'` &&  timedatectl set-timezone $tz
export tz=`timedatectl status| grep Timezone | awk '{print $2}'`
echo "TimeZone set to $tz"

# A loop that checks whether the output of the update command is as expected after successfull completion.
check_output_update_command() {
    #test_result=false
    while [[ $test_result != true ]]
    do
        update_output=$(yes | sudo apt update)
        echo $update_output

        allowed_results=("Reading package lists... Building dependency tree... Reading state information... All packages are up to date."
                "packages can be upgraded. Run 'apt list --upgradable' to see them."
            )
            
        test_result=$(actual_result_has_any_allowed_result_in_tail "$update_output" "${allowed_results[@]}")
        test_result=${test_result: -4}
        echo $test_result
    done
}
check_output_update_command "$@"
read -p "Succesfully completed update command <type enter>" next

# A loop that checks whether the output of the upgrade command is as expected after successfull completion.
check_output_upgrade_command() {
    #output_ending=false
    while [[ $output_ending != " upgraded." ]]
    do
        upgrade_output=$(yes | sudo apt upgrade)
        echo $upgrade_output

        
        #output_ending=$(tail -c 11 $upgrade_output)
        output_ending=${upgrade_output: -10}
        echo $output_ending
    done
}
check_output_upgrade_command "$@"
read -p "Succesfully completed upgrade command <type enter>" next

并且可以使用以下命令执行:

sudo mkdir /media/usbstick
sudo mount /dev/sda1 /media/usbstick
/media/usbstick/.first_run.sh

相关内容