Last active
January 12, 2019 11:05
-
-
Save photonxp/639e810f13ed7ce954f8b27c2e5bd351 to your computer and use it in GitHub Desktop.
Purge redundant Ubuntu kernel files by rules defined in the script, such as the number of kernels to be preserved
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# What if we could adjust the clock, | |
# making a day of a virtual AI world much shorter than ours? | |
# What if a virtual AI being could spend its whole long life | |
# in a blink of our eyes? | |
############################################################# | |
# Purpose: | |
# Purge redundant Ubuntu kernel files by rules defined in the script | |
# such as the number of kernels to be preserved | |
# Compatibility: | |
# Ubuntu 16, 18; Debian 8, 9 amd64 | |
# And probably some other debian distributions | |
# Usage: | |
# 1. Default mode, purge the redundant | |
# /path/to/purge_kernel_by_rules.bash | |
# 2. Manual arguments mode, selectively purge. | |
# It's also affected by default values *KERNELS_TO_PRESERVE. | |
# 2.1 One argument, no need for other kernel packages such as linux-header- | |
# /path/to/purge_kernel_by_rules.bash linux-image-333.16.0-6-amd64 | |
# /path/to/purge_kernel_by_rules.bash "linux-image-333.16.0-6-amd64" | |
# 2.2 Multiple arguments | |
# ./purge_kernel_by_rules.bash linux-image-333.16.0-6-amd64 linux-image-444.4.0-28-generic | |
# ./purge_kernel_by_rules.bash "linux-image-444.16.0-20-generic linux-image-444.16.0-20-generic" | |
# 3. Pipe mode | |
# 3.1 Simple pipe | |
# echo linux-image-333.16.0-6-amd64 linux-image-444.4.0-28-generic | ./purge_kernel_by_rules.bash | |
# 3.2 Pipe mixed with arguments input | |
# echo linux-image-333.16.0-6-amd64 | ./purge_kernel_by_rules.bash linux-image-444.4.0-28-generic | |
# The arguments input are listed and purged before the pipe. | |
# You can also change the number of oldest kernels or newest kernels | |
# that you'd like to preserve | |
# by adjusting the values of NUMBER_OF_OLDEST_KERNELS_TO_PRESERVE | |
# and NUMBER_OF_NEWEST_KERNELS_TO_PRESERVE | |
# By default 1 oldest and 2 newest along with the ruuning kernel | |
# are reserved when purge the kernels | |
# For the running kernel version: | |
# If it's in the range of oldest/newest kernels | |
# then the final reserve list will be generated by | |
# user-defined defalut values | |
# If it's not in the range of oldest/newest kernels | |
# then the final reserve list will be generated by | |
# (user-defined defalut values + running kernel version) | |
# The number of remaining kernels after purge | |
# shouldn't be less than the number of total kernels to preserve | |
#### WARNINGS: | |
#### This script could seriously damage your system or computer | |
#### if you use it improperly or carelessly | |
#### So, use it with cautions and at your own risks | |
#### A test environment is preferred | |
# ATTENTION: | |
# You'd have at least 1 kernel | |
# for your system to run | |
# If the numbers here are all 0 | |
# then only the running kernel is reserved | |
# and all the rest kernels are purged | |
SET_DEFAULT_VALUES_FOR_RESERVATION(){ | |
NUMBER_OF_OLDEST_KERNELS_TO_PRESERVE=1 | |
NUMBER_OF_NEWEST_KERNELS_TO_PRESERVE=2 | |
number_of_kernels_to_preserve=$((NUMBER_OF_OLDEST_KERNELS_TO_PRESERVE + NUMBER_OF_NEWEST_KERNELS_TO_PRESERVE)) | |
} | |
print_with_tail_newline(){ | |
printf "$1 \n\n" | |
} | |
notice_to_check(){ | |
echo | |
echo "NOTICE: Check your default values and reset them properly." | |
print_with_tail_newline "NOTICE: No removing kernels. Exit." | |
} | |
check_minimum_values_for_reservation(){ | |
if [ 0 -ge $number_of_kernels_to_preserve ] | |
then | |
echo | |
echo "** CAUTION: The number of non-running kernels to preserve is 0. " | |
fi | |
} | |
handle_default_values_for_reservation(){ | |
SET_DEFAULT_VALUES_FOR_RESERVATION | |
} | |
get_running_kernel_info(){ | |
sys_running=`uname -s` | |
ver_running=`uname -r` | |
# ver_running="4.4.0-134" | |
} | |
list_kernel_candidates(){ | |
print_with_tail_newline "NOTICE: Existing candidate kernel packages on your system:" | |
dpkg -l | grep ' linux-\(image\|headers\|image-extra\|signed-image\|modules\|modules-extra\)' | |
print_with_tail_newline "========================================" | |
issue_info=$(cat /etc/issue) | |
echo "Currently running:" | |
echo " $issue_info" | |
echo "$(echo ' '$sys_running $ver_running)" | |
} | |
get_script_args(){ | |
str_list_arg_input_LINUX_IMAGE="$@" | |
} | |
generate_OS_LINUX_IMAGE_list(){ | |
# all the linux-image-[ver] pkges on your system | |
str_list_OS_LINUX_IMAGE=$(dpkg --list | grep linux-image-[0-9] | awk '{ print $2 }' | sort -V | xargs echo) | |
arr_list_OS_LINUX_IMAGE=($str_list_OS_LINUX_IMAGE) | |
length_of_arr_list_OS_LINUX_IMAGE=${#arr_list_OS_LINUX_IMAGE[@]} | |
#print_with_tail_newline $length_of_arr_list_OS_LINUX_IMAGE | |
#print_with_tail_newline ${arr_list_OS_LINUX_IMAGE[0]} | |
} | |
check_OS_LINUX_IMAGE_list_length(){ | |
if [ $length_of_arr_list_OS_LINUX_IMAGE -le $number_of_kernels_to_preserve ] | |
then | |
echo "NOTICE: The number of found linux-image kernel versions" | |
echo "NOTICE: were less or equal to $number_of_kernels_to_preserve ." | |
notice_to_check | |
exit 1 | |
fi | |
} | |
check_empty_space_input(){ | |
# invalid input like " " | |
str=$(echo $str_input_list_LINUX_IMAGE) | |
if [ "y" == "y$str" ] | |
then | |
print_with_tail_newline "NOTICE: Invalid empty input::Empty kernel string. Exit." | |
exit 1 | |
fi | |
} | |
check_OS_LINUX_IMAGE_list_length_for_input(){ | |
INPUT_LINUX_IMAGE_list_arr=($str_input_list_LINUX_IMAGE) | |
INPUT_LINUX_IMAGE_list_arr_length=${#INPUT_LINUX_IMAGE_list_arr[@]} | |
number_of_kernels_to_remain=$((length_of_arr_list_OS_LINUX_IMAGE - INPUT_LINUX_IMAGE_list_arr_length)) | |
if [ $number_of_kernels_to_preserve -gt $number_of_kernels_to_remain ] | |
then | |
echo "NOTICE: The number of remaining versions of linux-image kernel" | |
echo "NOTICE: should be GREATER than $number_of_kernels_to_preserve" | |
echo "NOTICE: after purging the input." | |
notice_to_check | |
exit 1 | |
fi | |
} | |
get_running_image_str(){ | |
for image_str in ${arr_list_OS_LINUX_IMAGE[@]} | |
do | |
result=$(printf "$image_str" | grep -n "$ver_running") | |
if [ 0 -eq $? ] | |
then | |
# remove the leading "num:" part in the result | |
image_str_running="${result#*:}" | |
break | |
fi | |
done | |
#echo "arr_list_OS_LINUX_IMAGE: ${arr_list_OS_LINUX_IMAGE[@]}" | |
#echo "image_str_running: $image_str_running" | |
} | |
make_raw_reserve_list(){ | |
# make list for the oldest kernels to reserve | |
arr_reserve_list_oldest_LINUX_IMAGE=(${arr_list_OS_LINUX_IMAGE[@]:0:$NUMBER_OF_OLDEST_KERNELS_TO_PRESERVE}) | |
str_reserve_list_oldest_LINUX_IMAGE="${arr_reserve_list_oldest_LINUX_IMAGE[@]}" | |
# make list for the newest kernels to reserve | |
length=${#arr_list_OS_LINUX_IMAGE[@]} | |
start_idx_arr_reserve_list_newest_LINUX_IMAGE=$(($length - $NUMBER_OF_NEWEST_KERNELS_TO_PRESERVE)) | |
arr_reserve_list_newest_LINUX_IMAGE=(${arr_list_OS_LINUX_IMAGE[@]:$start_idx_arr_reserve_list_newest_LINUX_IMAGE}) | |
str_reserve_list_newest_LINUX_IMAGE="${arr_reserve_list_newest_LINUX_IMAGE[@]}" | |
reserve_list_raw="$str_reserve_list_oldest_LINUX_IMAGE $str_reserve_list_newest_LINUX_IMAGE" | |
} | |
make_reserve_list(){ | |
# add the running kernel version to the raw_reserve_list # | |
check_minimum_values_for_reservation | |
get_running_image_str | |
make_raw_reserve_list | |
printf "$reserve_list_raw" | grep -q "$image_str_running" | |
if [ 0 -ne $? ] | |
then | |
reserve_list="$image_str_running $reserve_list_raw" | |
else | |
reserve_list="$reserve_list_raw" | |
fi | |
print_with_tail_newline "Reserved kernel list: $reserve_list" | |
} | |
do_input(){ | |
# arg input, pipe input or pipe with arg input | |
str_input_list_LINUX_IMAGE=$1 | |
check_empty_space_input | |
check_OS_LINUX_IMAGE_list_length_for_input | |
arr_raw_purge_list_LINUX_IMAGE=($str_input_list_LINUX_IMAGE) | |
make_reserve_list | |
} | |
make_reserve_list_for_default(){ | |
# only show reserve list for terminal users | |
# not really used by the default mode | |
make_reserve_list | |
# This is the real reserve_list used by the default mode | |
get_running_image_str | |
reserve_list="$image_str_running" | |
} | |
do_default(){ | |
echo "NOTICE: Purge with default mode." | |
kernel_purge_list_length=$(($length_of_arr_list_OS_LINUX_IMAGE - $number_of_kernels_to_preserve)) | |
arr_raw_purge_list_LINUX_IMAGE=(${arr_list_OS_LINUX_IMAGE[@]:$NUMBER_OF_OLDEST_KERNELS_TO_PRESERVE:$kernel_purge_list_length}) | |
arr_raw_purge_list_LINUX_IMAGE_length=${#arr_raw_purge_list_LINUX_IMAGE[@]} | |
make_reserve_list_for_default | |
} | |
do_nopipe(){ | |
# manual argument mode, purge from user arguments input | |
if [ -n "$str_list_arg_input_LINUX_IMAGE" ] | |
then | |
echo "NOTICE: Purge with user arguments input mode." | |
do_input "$str_list_arg_input_LINUX_IMAGE" | |
fi | |
# default mode, no user-input arg | |
if [ -z "$str_list_arg_input_LINUX_IMAGE" ] | |
then | |
do_default | |
fi | |
} | |
do_pipe(){ | |
echo "NOTICE: Purge with user pipe (or pipe-mixed) input mode." | |
# cat from default stdin, which receive data from the pipe | |
str_pipe_lines_LINUX_IMAGE=$(cat | tr -s '\n' ' ') | |
str_list_pipe_input_LINUX_IMAGE="$str_list_arg_input_LINUX_IMAGE $str_pipe_lines_LINUX_IMAGE" | |
do_input "$str_list_pipe_input_LINUX_IMAGE" | |
} | |
how_to_do(){ | |
generate_OS_LINUX_IMAGE_list | |
check_OS_LINUX_IMAGE_list_length | |
# check pipe status | |
if [ -t 0 ] | |
then | |
do_nopipe | |
else | |
do_pipe | |
fi | |
} | |
init_purge_list_strs(){ | |
# 1 package for ubuntu and debian 8 amd64 | |
str_purge_list_LINUX_IMAGE="" | |
# 4 packages for ubuntu 16 | |
str_purge_list_LINUX_HEADERS="" | |
str_purge_list_LINUX_HEADERS_GENERIC="" | |
str_purge_list_LINUX_IMAGE_EXTRA="" | |
str_purge_list_LINUX_SIGNED_IMAGE="" | |
# 2 packages for ubuntu 18 | |
str_purge_list_LINUX_MODULES="" | |
str_purge_list_LINUX_MODULES_EXTRA="" | |
# all packages for both ubuntu and debian | |
kernel_purge_list_str="" | |
} | |
get_version_numbers_by_image_string(){ | |
image_ver=$(echo $image_string | cut -d - -f 3-4) | |
ver_n1=${image_ver%%.*} | |
ver_n1_n2=${image_ver%.*} | |
ver_n2=${ver_n1_n2#*.} | |
} | |
get_ubuntu_unique_kernels(){ | |
# change header kernel format from debian to ubuntu | |
arr_purge_list_LINUX_HEADERS_GENERIC[$j]=${debian_header_string//-common} | |
arr_purge_list_LINUX_HEADERS[$j]=${arr_purge_list_LINUX_HEADERS_GENERIC[$j]//-generic} | |
get_version_numbers_by_image_string | |
# ubuntu 18, kernel 4.15.0 | |
if [[ ("$ver_n1" -eq 4 && "$ver_n2" -ge 15) || "$ver_n1" -ge 5 ]] | |
then | |
arr_purge_list_LINUX_MODULES[$j]="${image_string/image/modules}" | |
arr_purge_list_LINUX_MODULES_EXTRA[$j]="${image_string/image/modules-extra}" | |
else | |
arr_purge_list_LINUX_IMAGE_EXTRA[$j]="${image_string//image/image-extra}" | |
arr_purge_list_LINUX_SIGNED_IMAGE[$j]="${image_string//image/signed-image}" | |
fi | |
} | |
get_linux_distribution_unique_kernels(){ | |
provider_id=${issue_info%% *} | |
#for test | |
#provider_id="Debian" | |
length=${#arr_raw_purge_list_LINUX_IMAGE[@]} | |
j=0 | |
for ((i=0;$i<$length;i++)) | |
do | |
image_string="${arr_raw_purge_list_LINUX_IMAGE[$i]}" | |
# skip if the kernel string is in the reserved kernel version list | |
printf "$reserve_list" | grep -q "$image_string" | |
if [ 0 -eq $? ] | |
then | |
continue | |
fi | |
#echo "j: $j" | |
# header for debian | |
debian_header_string="${image_string//image/headers}" | |
arr_purge_list_LINUX_HEADERS[$j]="$debian_header_string" | |
# image for debian, ubuntu | |
arr_purge_list_LINUX_IMAGE[$j]="$image_string" | |
# ubuntu 16, 18 | |
if [ "Ubuntu" == "$provider_id" ] | |
then | |
get_ubuntu_unique_kernels | |
fi | |
j=$((j+1)) | |
done | |
} | |
arr_to_str_purge_list(){ | |
str_purge_list_LINUX_HEADERS="${arr_purge_list_LINUX_HEADERS[@]}" | |
str_purge_list_LINUX_HEADERS_GENERIC="${arr_purge_list_LINUX_HEADERS_GENERIC[@]}" | |
str_purge_list_LINUX_IMAGE="${arr_purge_list_LINUX_IMAGE[@]}" | |
str_purge_list_LINUX_IMAGE_EXTRA="${arr_purge_list_LINUX_IMAGE_EXTRA[@]}" | |
str_purge_list_LINUX_SIGNED_IMAGE="${arr_purge_list_LINUX_SIGNED_IMAGE[@]}" | |
str_purge_list_LINUX_MODULES="${arr_purge_list_LINUX_MODULES[@]}" | |
str_purge_list_LINUX_MODULES_EXTRA="${arr_purge_list_LINUX_MODULES_EXTRA[@]}" | |
} | |
format_str_kernel_purge_list(){ | |
# change *_purge_list arr to str here | |
formatted_kernel_purge_list_str=" \ | |
$str_purge_list_LINUX_IMAGE \ | |
$str_purge_list_LINUX_HEADERS \ | |
$str_purge_list_LINUX_HEADERS_GENERIC \ | |
$str_purge_list_LINUX_IMAGE_EXTRA \ | |
$str_purge_list_LINUX_SIGNED_IMAGE \ | |
$str_purge_list_LINUX_MODULES \ | |
$str_purge_list_LINUX_MODULES_EXTRA" | |
} | |
print_if_no_empty_line(){ | |
str_purge_list_no_side_spaces="$(echo $1)" | |
if [ "y" != "y$str_purge_list_no_side_spaces" ] | |
then | |
echo "$str_purge_list_no_side_spaces" | |
fi | |
} | |
print_kernel_purge_list_by_type(){ | |
print_if_no_empty_line "$str_purge_list_LINUX_IMAGE" | |
echo | |
print_if_no_empty_line "$str_purge_list_LINUX_HEADERS" | |
print_if_no_empty_line "$str_purge_list_LINUX_HEADERS_GENERIC" | |
echo | |
print_if_no_empty_line "$str_purge_list_LINUX_IMAGE_EXTRA" | |
print_if_no_empty_line "$str_purge_list_LINUX_SIGNED_IMAGE" | |
echo | |
print_if_no_empty_line "$str_purge_list_LINUX_MODULES" | |
print_if_no_empty_line "$str_purge_list_LINUX_MODULES_EXTRA" | |
echo | |
} | |
show_kernel_purge_list_str_by_type(){ | |
print_with_tail_newline "NOTICE: Generated lists of all kernel packages to be purged:" | |
print_kernel_purge_list_by_type | |
} | |
generate_kernel_purge_list_str(){ | |
init_purge_list_strs | |
get_linux_distribution_unique_kernels | |
arr_to_str_purge_list | |
format_str_kernel_purge_list | |
show_kernel_purge_list_str_by_type | |
} | |
vital_check(){ | |
while true | |
do | |
echo "NOTICE: Check all the kernel versions above before the next step." | |
read -p "Press Ctrl+C to interrupt or press Enter to continue: " input | |
if [ -z "$input" ] | |
then | |
break | |
fi | |
done | |
print_with_tail_newline "Continued.. " | |
} | |
purge_kernels_by_purge_list_str(){ | |
purge_cmd="apt-get purge $formatted_kernel_purge_list_str" | |
print_with_tail_newline "$purge_cmd" | |
# grep command in this script could return err | |
# so set this arg in the later part of the script | |
set -e | |
$purge_cmd | |
update-grub2 | |
} | |
main(){ | |
handle_default_values_for_reservation | |
get_running_kernel_info | |
list_kernel_candidates | |
get_script_args "$@" | |
how_to_do | |
generate_kernel_purge_list_str | |
vital_check | |
purge_kernels_by_purge_list_str | |
} | |
################################## | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment