CentOS 7 learn used grub entry

With the major changes introduced in CentOS 7 from CentOS 6 (systemd, grub2, and more), determining exactly which grub menu entry will be used at next boot is a little more difficult than before.

I wrote a quick script that calculates this for you: learn-used-grub-entry

#!/bin/sh
# File: learn-used-grub-entry
# Location: /usr/bin
# Author: bgstack15
# Startdate: 2019-01-28 11:27
# Title: Script that Determines Which Grub Entry Will be Used
# Purpose:
# Package: bgscripts
# History:
# Usage:
#    for RHEL7/grub2
# Reference:
#    original research
# Improve:

test -z "${LUGE_GRUB_CFG}" && LUGE_GRUB_CFG=/boot/grub2/grub.cfg
test -z "${LUGE_ETC_DEFAULT_GRUB}" && LUGE_ETC_DEFAULT_GRUB=/etc/default/grub
# use option LUGE_OUTPUT which is one of ["kernel","","initram"]

grub_saved_name="$( grub2-editenv - list | awk -F'=' '{print $NF}' )"
use_number="$( awk -F'=' '/^GRUB_DEFAULT/{print $NF}' "${LUGE_ETC_DEFAULT_GRUB}" )"

use_entry=""
# calculate true value
if test "$( echo "${use_number}" | tr '[[:upper:]]' '[[:lower:]]' )" = "saved" ;
then
   # it is the saved value (which is same as last used)
   use_entry="${grub_saved_name}"
else
   # calculate used name from number
   use_entry="$( grep -E -e '^menuentry' "${LUGE_GRUB_CFG}" | sed -r -n -e "$(( use_number + 1 ))p" )"
fi

# calculate which value to display or just display name
LUGE_grep=1
case "${LUGE_OUTPUT:-${1}}" in

   "num" | "number" )
      # just show the number
      unset LUGE_grep
      echo "${use_number}"
      ;;

   "kernel"|"vmlinuz" )
      # show kernel
      LUGE_regex="linux(16|32|64)"
      LUGE_awk_val='$2'
      ;;

   "initram" | "initrd" | "initramfs" )
      # show initram
      LUGE_regex="initrd|initram"
      LUGE_awk_val='$2'
      ;;

   *)
      # DEFAULT VALUE, so mistyped or not defined
      unset LUGE_grep
      echo "${use_entry}"
      ;;
esac

# show complicated value if necessary
if test -n "${LUGE_grep}" ;
then
   sed -n -r -e "/${use_entry}/,/^\s*\}/p" "${LUGE_GRUB_CFG}" | awk "/${LUGE_regex}/{print ${LUGE_awk_val}}"
fi

# exit cleanly
true

And an ansible playbook for myself:

---
# File: luge-tasks.yml
# Purpose: provides common tasks for displaying output from learn-used-grub-entry.sh
# Dependencies:
#    vars:
#       luge_script: '/etc/ansible/files/learn-used-grub-entry.sh'

- name: LUGE - learn lvm.conf filter
  shell: grep -E -e "^\s*(global_)?filter" {{ lvm_conf_file | default('/etc/lvm/lvm.conf') }} ; true
  args:
    warn: no
  changed_when: false
  register: lvm_conf

- name: LUGE - learn /boot value in fstab
  shell: grep -hE -e '\/boot' {{ etc_fstab_file | default('/etc/fstab') }} | grep -viE '^\s*(#|$)' ; true
  args:
    warn: no
  changed_when: false
  register: etc_fstab

- name: LUGE - learn grub menuentry to be used
  script: "{{ luge_script | default('/etc/ansible/files/learn-used-grub-entry.sh') }}"
  environment:
    LUGE_OUTPUT: name
  changed_when: false
  register: luge_menuentry

- name: LUGE - learn grub menuentry initram to be used
  script: "{{ luge_script | default('/etc/ansible/files/learn-used-grub-entry.sh') }}"
  environment:
    LUGE_OUTPUT: initram
  changed_when: false
  register: luge_initram

# this one will fail if the file does not exist
- name: LUGE - fail if selected initram is not valid initram file
  shell: lsinitrd "/boot{{ luge_initram.stdout_lines[0] }}" 1>/dev/null
  args:
    warn: no
  changed_when: false

- name: LUGE - capture raw info
  shell: echo "{{ item }}"
  loop:
  - "lvm.conf: {{ lvm_conf.stdout }}"
  - "fstab: {{ etc_fstab.stdout }}"
  - "menuentry: {{ luge_menuentry.stdout }}"
  - "initram: {{ luge_initram.stdout }}"
  register: stdout1
  changed_when: false

- name: LUGE - show useful info
  debug:
    msg: "{{ stdout1.results|map(attribute='stdout_lines')|flatten(levels=1) }}"

# info that is useful that is generated by the above statements
# - "{{ lvm_conf.stdout }}"
# - "{{ etc_fstab.stdout }}"
# - "{{ luge_menuentry.stdout }}"
# - "{{ luge_initram.stdout }}"

Update /etc/default/grub programmatically

My latest bgconf package includes my grub preferences for RHEL-based systems.

I allow for environment variable overrides, but the defaults are to disable rhgb and quiet. If you have been looking for a way to easily remove red hat graphical boot (rhgb) so you can actually see the kernel messages scroll by, you want this script.

#!/bin/sh

# DEFINE FUNCTIONS

clean_mgm() {
   rm -rf "${MGM_TMPDIR}" 2>/dev/null
}

update_grub_if_changed() {
   # call: update_grub_if_changed "${MGM_INFILE}" "${MGM_TMPFILE1}"

   local infile="${1}"
   local tmpfile="${2}"

   # determine if changes were made to the file
   if diff -q "${infile}" "${tmpfile}" 2>&1 | grep -qiE 'differ' ;
   then
      # changes were made
      if fistruthy "${MGM_APPLY}" ;
      then
         sudo /bin/cp -p "${tmpfile}" "${infile}"
         sudo /usr/sbin/grub2-mkconfig -o "${MGM_GRUB_FILE:-/boot/grub2/grub.cfg}"
      fi
   else
      # no changes
      :
   fi

}

add_value_to_grub_line() {
   # call: add_value_to_grub_line "${MGM_TMPFILE1}" "GRUB_CMDLINE_LINUX" "quiet"

   local infile="${1}"
   local thisvar="${2}"
   local thisvalue="${3}"

   sed -i -r -e "/^${thisvar}=/{ /${thisvalue}/! { s/\"\s*\$/${thisvalue}\"/; } ; }" "${infile}"

}

remove_value_from_grub_line() {
   # call: remove_value_from_grub_line "${MGM_TMPFILE1}" "GRUB_CMDLINE_LINUX" "quiet"

   local infile="${1}"
   local thisvar="${2}"
   local thisvalue="${3}"

   sed -i -r -e "/^${thisvar}=/{ /${thisvalue}/ { s/\s*${thisvalue}//; } ; }" "${infile}"

}

# REACT TO OS
# fail out if not rhel-based
. /usr/share/bgscripts/framework.sh
case "${thisflavor}" in
   fedora|el|centos|korora) : ;;
   *) exit 0 ;;
esac

# set variables
test -z "${MGM_TMPDIR}" && MGM_TMPDIR="$( mktemp -d )"
test -z "${MGM_TMPFILE1}" && MGM_TMPFILE1="$( TMPDIR="${MGM_TMPDIR}" mktemp )"

test -z "${MGM_INFILE}" && MGM_INFILE=/etc/default/grub
test -z "${MGM_GRUB_FILE}" && MGM_GRUB_FILE=/boot/grub2/grub.cfg
test -z "${MGM_APPLY}" && MGM_APPLY=yes
test -z "${MGM_RHGB}" && MGM_RHGB=no
test -z "${MGM_QUIET}" && MGM_QUIET=no

# clean up temp file if necessary
test ! -e "${MGM_TMPFILE1}" && { touch "${MGM_TMPFILE1}" || exit 1 ; }
cat "${MGM_INFILE}" > "${MGM_TMPFILE1}"
trap '__ec=$? ; clean_mgm ; trap "" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; exit ${__ec:-0} ;' 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

# Do the RHGB action
if fistruthy "${MGM_RHGB}" ;
then
   # add rhgb to grub config
   add_value_to_grub_line "${MGM_TMPFILE1}" "GRUB_CMDLINE_LINUX" "rhgb"
else
   # remove rhgb
   remove_value_from_grub_line "${MGM_TMPFILE1}" "GRUB_CMDLINE_LINUX" "rhgb"
fi

# Do the QUIET action
if fistruthy "${MGM_QUIET}" ;
then
   # add quiet to grub config
   add_value_to_grub_line "${MGM_TMPFILE1}" "GRUB_CMDLINE_LINUX" "quiet"
else
   # remove quiet
   remove_value_from_grub_line "${MGM_TMPFILE1}" "GRUB_CMDLINE_LINUX" "quiet"
fi

# Determine if any changes occurred to the file
update_grub_if_changed "${MGM_INFILE}" "${MGM_TMPFILE1}"

# show final results
fistruthy "${MGM_VERBOSE}" && cat "${MGM_TMPFILE1}"