My code in message #16 generated an invalid grub.cfg if one of the detected
Linux OSes had no submenu items.

Attached is a diff of an updated os-prober script which fixes that problem.
--- 30_os-prober        2016-07-22 20:09:25.000000000 -0400
+++ 15_os-prober        2017-03-07 19:55:47.108820452 -0500
@@ -27,6 +27,48 @@
 
 . "${datarootdir}/grub/grub-mkconfig_lib"
 
+safe_echo () { printf %s\\n "$*" ; }
+safe_echo_n () { printf %s "$*" ; }
+# Based on http://www.etalabs.net/sh_tricks.html
+
+rtrim() {  # remove trailing whitespace characters 
+  local var="$*"
+  var="${var%"${var##*[![:space:]]}"}"
+  safe_echo_n "$var"
+}
+# The shell functions rtrim, ltrim, trim are based on 
+# http://stackoverflow.com/a/3352015 
+
+ltrim() {  # remove leading whitespace characters 
+  local var="$*"
+  var="${var#"${var%%[![:space:]]*}"}"
+  safe_echo_n "$var"
+}
+
+trim() {  # remove leading & trailing whitespace 
+  local var="$*"
+  var=$(ltrim "$(rtrim "$var")")
+  safe_echo_n "$var"
+}
+
+name_to_compare () { echo "${1}" | tr -c "[:alpha:]" " " | tr -s " " | \
+                     tr "[:upper:]" "[:lower:]" | \
+                     sed "s/[ ]\+$//" ; }
+# This function is used in detecting whether an /etc/lsb-release file's 
+# DISTRIB_CODENAME is redundantly included at the end of the file's 
+# DISTRIB_DESCRIPTION.  
+#   Convert all non-letters in ${1} to spaces.  
+#   Then convert multiple consecutive spaces to single spaces.  
+#   Then convert all uppercase letters to lowercase.  
+#   Then remove trailing spaces if present (sed "s/ $//" doesn't seem to work 
+# for this).  
+#   We do these things so an LCODENAME of "betsy" will be detected at the end 
+# of an LDESC of "LMDE 2 (Betsy)", but not in an LDESC of "LMDE blah 
b_e.t-s.y" 
+        
+menu_entry_values () { echo ${1} | cut -d ':' --complement -f 3 ; }
+# Get the values (other than the displayed name) of a menu entry.  
+# This shell function is used to detect whether 2 menu entries are the same.  
+
 found_other_os=
 
 adjust_timeout () {
@@ -244,74 +286,154 @@
           LLABEL="${LONGNAME}"
         fi
 
-       if [ "${LROOT}" != "${LBOOT}" ]; then
-         LKERNEL="${LKERNEL#/boot}"
-         LINITRD="${LINITRD#/boot}"
-       fi
+        if [ "${LROOT}" != "${LBOOT}" ]; then
+          LKERNEL="${LKERNEL#/boot}"
+          LINITRD="${LINITRD#/boot}"
+        fi
 
-       if [ -z "${prepare_boot_cache}" ]; then
-         prepare_boot_cache="$(prepare_grub_to_access_device ${LBOOT} | 
grub_add_tab)"
-         [ "${prepare_boot_cache}" ] || continue
-       fi
+        if [ -z "${prepare_boot_cache}" ]; then
+          prepare_boot_cache="$(prepare_grub_to_access_device ${LBOOT} | 
grub_add_tab)"
+          [ "${prepare_boot_cache}" ] || continue
+        fi
 
-       found_other_os=1
-       onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
-       recovery_params="$(echo "${LPARAMS}" | grep 'single\|recovery')" || true
-       counter=1
-       while echo "$used_osprober_linux_ids" | grep 
'osprober-gnulinux-$LKERNEL-${recovery_params}-$counter-$boot_device_id' > 
/dev/null; do
-           counter=$((counter+1));
-       done
-       if [ -z "$boot_device_id" ]; then
-           boot_device_id="$(grub_get_device_id "${DEVICE}")"
-       fi
-       used_osprober_linux_ids="$used_osprober_linux_ids 
'osprober-gnulinux-$LKERNEL-${recovery_params}-$counter-$boot_device_id'"
+        LSB_QUERY_MOUNT_POINT=$(findmnt -flno TARGET ${DEVICE})
 
-       if [ "x$is_top_level" = xtrue ] && [ "x${GRUB_DISABLE_SUBMENU}" != xy 
]; then
-            cat << EOF
+        if [ -z "${LSB_QUERY_MOUNT_POINT}" ] ; then
+          LSB_QUERY_MOUNT_POINT=$(mktemp -d)
+          mount ${DEVICE} ${LSB_QUERY_MOUNT_POINT}
+          TEMP_MOUNT="true"
+          # It appears that this code doesn't run:  Although syslog indicates 
+          # that /usr/lib/os-probes/mounted/40lsb indeed scans unmounted 
+          # partitions, it seems that /usr/bin/os-prober (which is indirectly 
+          # the parent process of "40lsb") still doesn't report any OS that 
+          # exists on such a partition.  The partition tested was formatted as 
+          # btrfs.  
+        else
+          TEMP_MOUNT="false"
+        fi
+
+        LSB_DATABASE=${LSB_QUERY_MOUNT_POINT}/etc/lsb-release
+
+        LCODENAME=$(grep ^DISTRIB_CODENAME ${LSB_DATABASE} | cut -d "=" -f 2)
+        LDESC=$(grep ^DISTRIB_DESCRIPTION ${LSB_DATABASE} | cut -d "=" -f 2)
+
+        if [ "x${TEMP_MOUNT}" = xtrue ] ; then
+          umount ${LSB_QUERY_MOUNT_POINT}
+        fi
+
+        if safe_echo "${LDESC}" | grep -E "^(['\"]).*\1$" >/dev/null; then
+          LDESC=$(safe_echo "${LDESC}" | cut -c 2- | rev | cut -c 2- | rev)
+        fi  # If ${LDESC} is quoted, then remove the quote marks.  
+
+        LSPECIAL_MODE=$(rtrim "$(safe_echo "$LLABEL" | \
+                                 grep -Eo "\([^(]*\)[[:space:]]*$")")
+        # Get any parenthesized string (optionally followed by whitespace) at 
+        # the end of $LLABEL.  
+
+        if [ -n "${LSPECIAL_MODE}" ]; then
+          LSPECIAL_MODE=" ${LSPECIAL_MODE}"
+        fi
+        # After this "if", $LSPECIAL_MODE either is null or starts with " ("  
+
+        LKERNEL_DISPLAY=$(safe_echo "${LKERNEL}" | cut -d "-" -f 2-)
+        # Try to extract the version number of the kernel image file.  
+
+        LDESC_CMP=$(name_to_compare "${LDESC}")
+        LCODENAME_CMP=$(name_to_compare "${LCODENAME}")
+
+        if safe_echo "${LDESC_CMP}" | grep -E "${LCODENAME_CMP}$" >/dev/null; 
then
+          # ${LDESC} ends in ${LCODENAME}.  
+          OS_SUBMENU="${LDESC}"
+        else
+          # ${LDESC} doesn't end in ${LCODENAME}.  
+          OS_SUBMENU="${LDESC} \"${LCODENAME}\""
+        fi
+
+        OS="${OS_SUBMENU}${LSPECIAL_MODE}, with Linux kernel 
${LKERNEL_DISPLAY}"
+        # Examples:  
+        # Ubuntu 13.04 "raring", with Linux kernel 3.8.0-35-generic
+        # LMDE 2 Betsy, with Linux kernel 3.16.0-4-amd64
+        #   (if codename is "betsy" & description ends in "Betsy")
+
+        found_other_os=1
+        onstr="$(gettext_printf "(on %s)" "${DEVICE}")"
+        recovery_params="$(echo "${LPARAMS}" | grep 'single\|recovery')" || 
true
+        counter=1
+        while echo "$used_osprober_linux_ids" | grep 
'osprober-gnulinux-$LKERNEL-${recovery_params}-$counter-$boot_device_id' > 
/dev/null; do
+          counter=$((counter+1));
+        done
+        if [ -z "$boot_device_id" ]; then
+          boot_device_id="$(grub_get_device_id "${DEVICE}")"
+        fi
+        used_osprober_linux_ids="$used_osprober_linux_ids 
'osprober-gnulinux-$LKERNEL-${recovery_params}-$counter-$boot_device_id'"
+
+        if [ "x$is_top_level" = xtrue ] && [ "x${GRUB_DISABLE_SUBMENU}" != xy 
]; then
+          printf "Adding '%s' to menu\n" "${OS}" >&2
+          cat << EOF
 menuentry '$(echo "$OS $onstr" | grub_quote)' --class gnu-linux --class gnu 
--class os \$menuentry_id_option 'osprober-gnulinux-simple-$boot_device_id' {
 EOF
-           save_default_entry | grub_add_tab
-           printf '%s\n' "${prepare_boot_cache}"
-           cat <<  EOF
+          save_default_entry | grub_add_tab
+          printf '%s\n' "${prepare_boot_cache}"
+          cat <<  EOF
        linux ${LKERNEL} ${LPARAMS}
 EOF
-            if [ -n "${LINITRD}" ] ; then
-          cat << EOF
+          if [ -n "${LINITRD}" ] ; then
+            cat << EOF
        initrd ${LINITRD}
 EOF
-            fi
-        cat << EOF
+          fi
+          cat << EOF
 }
 EOF
-           echo "submenu '$(gettext_printf "Advanced options for %s" "${OS} 
$onstr" | grub_quote)' \$menuentry_id_option 
'osprober-gnulinux-advanced-$boot_device_id' {"
-           is_top_level=false
-       fi
-       title="${LLABEL} $onstr"
-        cat << EOF
-       menuentry '$(echo "$title" | grub_quote)' --class gnu-linux --class gnu 
--class os \$menuentry_id_option 
'osprober-gnulinux-$LKERNEL-${recovery_params}-$boot_device_id' {
+          if safe_echo $(trim "${LINUXPROBED}") | grep -E "[[:blank:]]" ; then
+            # Does $LINUXPROBED contain spaces or tabs other than at the ends? 
 
+            # Only if so (multiple words in $LINUXPROBED) should a submenu 
+            # header be emitted.  
+
+            echo "submenu '$(gettext_printf "  Advanced options for %s" 
"${OS_SUBMENU} $onstr" | grub_quote)' \$menuentry_id_option 
'osprober-gnulinux-advanced-$boot_device_id' {"
+
+            PRIMARY_VALUES="$(menu_entry_values ${LINUX})"
+            # Stash the values (other than the displayed name) of the primary 
+            # menu entry.  
+
+            is_top_level=false
+          fi
+        fi
+
+        SUBMENU_VALUES="$(menu_entry_values ${LINUX})"
+        if [ "x${is_top_level}" = xfalse ] && \
+           [ "x${SUBMENU_VALUES}" != "x${PRIMARY_VALUES}" ]; then
+          # Allow only submenu entries that are different than their primary 
+          # menu entry.  
+
+          title="${OS} $onstr" 
+          printf "Adding '%s' to submenu\n" "${OS}" >&2 
+          cat << EOF
+  menuentry '$(echo "$title" | grub_quote)' --class gnu-linux --class gnu 
--class os \$menuentry_id_option 
'osprober-gnulinux-$LKERNEL-${recovery_params}-$boot_device_id' {
 EOF
-       save_default_entry | sed -e "s/^/$grub_tab$grub_tab/"
-       printf '%s\n' "${prepare_boot_cache}" | grub_add_tab
-       cat <<  EOF
-               linux ${LKERNEL} ${LPARAMS}
+          save_default_entry | sed -e "s/^/$grub_tab$grub_tab/"
+          printf '%s\n' "${prepare_boot_cache}" | grub_add_tab
+          cat << EOF
+    linux ${LKERNEL} ${LPARAMS}
 EOF
-        if [ -n "${LINITRD}" ] ; then
+          if [ -n "${LINITRD}" ] ; then
             cat << EOF
-               initrd ${LINITRD}
+    initrd ${LINITRD}
 EOF
-        fi
-        cat << EOF
-       }
+          fi
+          cat << EOF
+  }
 EOF
-       if [ x"$title" = x"$GRUB_ACTUAL_DEFAULT" ] || [ x"Previous Linux 
versions>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then
-           replacement_title="$(echo "Advanced options for ${OS} $onstr" | sed 
's,>,>>,g')>$(echo "$title" | sed 's,>,>>,g')"
-           quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)"
-           title_correction_code="${title_correction_code}if [ \"x\$default\" 
= '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;"
-           grub_warn "$(gettext_printf "Please don't use old title \`%s' for 
GRUB_DEFAULT, use \`%s' (for versions before 2.00) or \`%s' (for 2.00 or 
later)" "$GRUB_ACTUAL_DEFAULT" "$replacement_title" 
"gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id")"
-       fi
+          if [ x"$title" = x"$GRUB_ACTUAL_DEFAULT" ] || [ x"Previous Linux 
versions>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then
+            replacement_title="$(echo "  Advanced options for ${OS} $onstr" | 
sed 's,>,>>,g')>$(echo "$title" | sed 's,>,>>,g')"
+            quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)"
+            title_correction_code="${title_correction_code}if [ \"x\$default\" 
= '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;"
+            grub_warn "$(gettext_printf "Please don't use old title \`%s' for 
GRUB_DEFAULT, use \`%s' (for versions before 2.00) or \`%s' (for 2.00 or 
later)" "$GRUB_ACTUAL_DEFAULT" "$replacement_title" 
"gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id")"
+          fi
+        fi
       done
       if [ x"$is_top_level" != xtrue ]; then
-         echo '}'
+        echo '}'
       fi
       echo "$title_correction_code"
     ;;

Reply via email to