skip to Main Content

For a command such as

grubby --info=ALL

the output received is something of the sort –

index=3
kernel="/boot/vmlinuz-4.18.0-80.el8.x86_64"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-4.18.0-80.el8.x86_64.img"
title="CentOS Linux (4.18.0-80.el8.x86_64) 8 (Core)"
id="d7fe995b9d09403896e1e56a2b02a947-4.18.0-80.el8.x86_64"
index=4
kernel="/boot/vmlinuz-0-rescue-d7fe995b9d09403896e1e56a2b02a947"
args="ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet"
root="/dev/mapper/cl-root"
initrd="/boot/initramfs-0-rescue-d7fe995b9d09403896e1e56a2b02a947.img"
title="CentOS Linux (0-rescue-d7fe995b9d09403896e1e56a2b02a947) 8 (Core)"
id="d7fe995b9d09403896e1e56a2b02a947-0-rescue"

I can pipe this output into an array, to try and refer to each block individually –

mapfile -t my_array < <(grubby --info=ALL )

However, this saves each element as a separate string in my_array, such as

 printf '%sn' "${my_array[0]}"

Output –

index=3

Perhaps I can access these elements for comparison based on regularity of offset (7 in this case for each subitem in subsequent blocks).

However, I’d like to retrieve the string value of these array components, doing a
printf '%sn' "${my_array[1]}" gives me
kernel="/boot/vmlinuz-4.18.0-80.el8.x86_64" from which I’d like to get the value…

Also, if someone could suggest a better way, such as by accessing the value individually in each file, maybe something like –

cd /boot/loader/entries
for filename in $(find -type f -name '*.conf'); do
//Access this field

Not sure how to do it, though..

2

Answers


  1. Subject to the comments about your output of grubby containing multiple index= lines where the name=value pairs have the same names under each index, the general way you handle parsing values from string variables in bash is with a parameter expansion (with substring removal). I say "general" way because the following parameter expansions are also provided in POSIX shell so your script will be portable to other shells. (bash provides an additional number of expansions that are bash-only)

    A summary of the parameter expansions with substring removal are:

    ${var#pattern}      Strip shortest match of pattern from front of $var
    ${var##pattern}     Strip longest match of pattern from front of $var
    ${var%pattern}      Strip shortest match of pattern from back of $var
    ${var%%pattern}     Strip longest match of pattern from back of $var
    

    (note: pattern can contain the normal globbing characters such as '*', and front above is the same as"from the left" and back is "from the right" which you will see used interchangeably)

    For your output above, you can loop over the lines separating the name=value pairs into name and value. Since the names repeat under each index, you can’t use an associative array array[name]="value" directly or you will only end up with the last values. (you can save the index=X and use array[X name]="value", but that gets messy when you want to retrieve things)

    You have another caveat with the args name that contains '=' within the value portion. (which you would want to use the ${var#pattern} form to isolate name=value based on the first '=' from the front (left))

    As an example, you could redirect the output of grubby directly as you have done using a process substitution (bash-only) or redirect it to a file and read line from the file with something similar to:

    #!/bin/bash
    
    while read -r line; do
        name="${line%%=*}"          ## strip from the right to last =
        value="${line#*=}"          ## strip from left through first =
        ## if double quoted -- remove double quotes
        [ "${value:0:1}" = '"' ] && value="${value:1:$((${#value}-2))}"
        printf "name: %-8s  value: '%s'n" "$name" "$value"
    done < "$1"
    

    Example Use/Output

    Reading your grubby data from a file would result in:

    $ bash readnameval.sh info
    name: index     value: '3'
    name: kernel    value: '/boot/vmlinuz-4.18.0-80.el8.x86_64'
    name: args      value: 'ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet'
    name: root      value: '/dev/mapper/cl-root'
    name: initrd    value: '/boot/initramfs-4.18.0-80.el8.x86_64.img'
    name: title     value: 'CentOS Linux (4.18.0-80.el8.x86_64) 8 (Core)'
    name: id        value: 'd7fe995b9d09403896e1e56a2b02a947-4.18.0-80.el8.x86_64'
    name: index     value: '4'
    name: kernel    value: '/boot/vmlinuz-0-rescue-d7fe995b9d09403896e1e56a2b02a947'
    name: args      value: 'ro crashkernel=auto resume=/dev/mapper/cl-swap rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet'
    name: root      value: '/dev/mapper/cl-root'
    name: initrd    value: '/boot/initramfs-0-rescue-d7fe995b9d09403896e1e56a2b02a947.img'
    name: title     value: 'CentOS Linux (0-rescue-d7fe995b9d09403896e1e56a2b02a947) 8 (Core)'
    name: id        value: 'd7fe995b9d09403896e1e56a2b02a947-0-rescue'
    

    So that is one way to approach the separation. The other would be to use awk which allows the same approach to simulating 2D arrays using a ',' to separate multiple index values (see SUBSEP in man awk). However, if you can get what you need without storing all values — then you eliminate the simulated 2D array issue altogether.

    Look things over and let me know if you have further questions.

    Login or Signup to reply.
  2. In bash, it is possible to create an associative array, say named grubby, and access its elements like ${grubby[index,key]}. For instance ${grubby[3,kernel]} should expand to /boot/vmlinuz-4.18.0-80.el8.x86_64.

    Example script:

    #!/bin/bash
    
    declare -A grubby
    
    while read -r line; do
        if [[ $line = index=* ]]; then
            index=${line#index=}
            continue
        fi
        [[ $line = *=* ]] || continue
        key=${line%%=*}
        value=${line#*=}
        value=${value#"}
        value=${value%"}
        grubby[$index,$key]=$value
    done
    # Examples
    echo "3,kernel = ${grubby[3,kernel]}"
    echo "4,root   = ${grubby[4,root]}"
    

    The output of the grubby command should be redirected to the script:

    grubby --info=ALL | ./script
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search