3

Final answer is already posted

Before reading the entire question note the answer below.

Short version of question

In Bash how can I find the next item in this file?

  • .../640x480-a.jpg
  • .../640x480-b.jpg
  • .../640x480-c.jpg

When the current item is embedded in this string?

GRUB_BACKGROUND="/home/rick/Pictures/Wallpaper/640x480-a.jpg"

If the current item is the last file entry, then the first file entry needs to be picked. The new entry needs to be updated in grub for the next boot.

What has been done so far

I have multiple background images for Grub. Manually editing /etc/default/grub to change the background image and then running sudo update-grub is time consuming and I'm likely to forget to do it. I would like it automatically done every boot / reboot.

I've setup a cron job called /etc/cron.d/cycle-grub-background containing:

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
@reboot   root    /usr/local/bin/cron-reboot-cycle-grub-background

I trust this job will run every reboot and not daily because sometimes the laptop isn't rebooted for many days and I don't want to skip over the available grub background images.

Ignore the code below filled with assumptions and dangerous errors

The following code is only provided for historical reference. Please do not use any of it as it is badly broken and of poor design.

The file /usr/local/bin/cron-reboot-cycle-grub-background contains:

#!/bin/bash

# NAME: cron-reboot-cycle-grub-background
# DATE: February 18, 2017
# PATH: /usr/local/bin/

# DESC: Cycle through available 640x480 wallpaper for grub background

# Get list of all grub images when 1920x1080 monitor has been downgraded
# to more readable 640x480 pixel format
ls /home/rick/Pictures/Wallpaper/640x480* > /tmp/grub-backgrounds

# Find this boot grub background image name
cat /etc/default/grub | grep BACKGROUND > /tmp/grub-background

# case? to find current background in backgrounds?
# Can we run systemd inhibit lock to prevent shutdown or reboot now?
# sed? to change to next background in /etc/default/grub

# Copy /etc/default/grub to /boot/grub/grub.cfg is the same as
# running sudo update-grub with out the 12 second delay

cp /boot/grub/grub.cfg /boot/grub/grub.cfg~
cp /etc/default/grub   /boot/grub/grub.cfg
# Can we release systemd inhibitor now?

# Now next reboot will have new background image
exit 0

Let's say the file /tmp/grub-backgrounds contains:

640x480-a.jpg
640x480-b.jpg
640x480-c.jpg

Let's say the file /tmp/grub-background contains:

GRUB_BACKGROUND="/home/rick/Pictures/Wallpaper/640x480-a.jpg"

Then the next picture on the list would be ...640x480-b.jpg.

But if the the current picture is ...c.jpg then the next picture resets to beginning of list and should be ...a.jpg.

I need to square this circle as it were. I think some sort of file case followed by sed is in order (as you can see by the code comments) but I can't quite wrap my mind around it.

As always, I'm grateful for any and all ideas.

  • 2
    Any reason you don't want to cyclically rename the files, and keep the GRUB_BACKGROUND entry the same? – Zeiss Ikon Feb 19 '17 at 14:42
  • Grub is holding the last file name used. If not stored there it would have to be stored somewhere else because we need to use that to find the next alphabetical file name on the list of all files every boot. – WinEunuuchs2Unix Feb 19 '17 at 14:47
2

A simple script, run from any of your system's startup scripts, can allow stepping through the wallpaper images in a particular folder, one step per restart. It's much faster than using update-grub and much simpler logic, plus it never needs to run update-grub. The downsides are that if you want to add an image to the list, you have to edit the script or write a slightly more complex one to handle variable list length, and you can't tell from the file names what image is in each file.

mv wallpaper3.png wallpaperx.png
mv wallpaper2.png wallpaper3.png
mv wallpaper1.png wallpaper2.png
mv wallpaperx.png wallpaper1.png

Even with a list of dozens of files, a longer, real-world version of this script ought to execute in a fraction of a second on an SSD, or in a couple seconds on a platter drive. It's simple, the logic is obvious, and you won't wonder, months or years later, what this script called by your startup is supposed to do.

| improve this answer | |
2

Short Answer

#!/bin/bash

CURR_FILE=$(cat /etc/default/grub | grep BACKGROUND) # Get grub current line
CURR_FILE=$(cut -d "=" -f 2 <<< "$CURR_FILE")        # File name only
CURR_FILE=$(echo "$CURR_FILE" | tr -d '"')           # Remove double quotes

for ALL_FILES in /home/rick/Pictures/Wallpaper/640x480*; do # Loop through every file
    if [[ "$FIRST_FILE" == "" ]]; then
        FIRST_FILE="$ALL_FILES"
    elif [[ "$MATCH_FILE" != "" ]]; then
        NEXT_FILE="$ALL_FILES"
        break # We've got it!
    fi
    if [[ "$CURR_FILE" == "$ALL_FILES" ]]; then
        MATCH_FILE="$ALL_FILES" # We found our current file entry
    fi
done

# If $NEXT_FILE empty we hit end of list so use First file name
if [[ "$NEXT_FILE" == "" ]]; then
    NEXT_FILE="$FIRST_FILE"
fi

# replace background file name in grub source file
sed -i "s|$CURR_FILE|$NEXT_FILE|g" /etc/default/grub

# replace background file name in grub configuration file
# Backup... just in case :)
cp /boot/grub/grub.cfg /boot/grub/grub.cfg~
# Short cut so we don't have to run `sudo update-grub`
sed -i "s|$CURR_FILE|$NEXT_FILE|g" /boot/grub/grub.cfg

Credits

After much googling this morning along with trial and error the problem was 90% solved. Then with incredible assistance in chat room from Pilot6, Terdon and Zanna the hardest part for me (using sed) was solved.

Considerate comments were also posted by cl-netbox and Byte Commander making Ask Ubuntu's chat room the friendliest most homogeneous technical chat room I've frequented in my two decades on the net.

Call script every boot with cron

Create the file /etc/cron.d/cycle-grub-background containing:

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
@reboot   root    /usr/local/bin/cron-reboot-cycle-grub-background

NOTE: create file using sudo powers. No need to mark it as executable but doing so won't hurt either.

Long Answer with debug and variable declarations

#!/bin/bash

# NAME: cron-reboot-cycle-grub-background
# DATE: February 18, 2017. Modified April 9, 2017.
# PATH: /usr/local/bin/

# DESC: Cycle through available wallpaper for grub background


CURR_FILE=$(cat /etc/default/grub | grep BACKGROUND) # Get grub current line
echo "Grub line:    $CURR_FILE"

CURR_FILE=$(cut -d "=" -f 2 <<< "$CURR_FILE") # File name only
CURR_FILE=$(echo "$CURR_FILE" | tr -d '"')    # Remove double quotes

echo "Current file: $CURR_FILE"

FIRST_FILE=""
NEXT_FILE=""
MATCH_FILE=""

for ALL_FILES in /home/rick/Pictures/1600x900/*; do # Loop through every file
    if [[ "$FIRST_FILE" == "" ]]; then
        FIRST_FILE="$ALL_FILES"
    fi
    if [[ "$MATCH_FILE" != "" ]]; then
        NEXT_FILE="$ALL_FILES"
        break # We've got it!
    fi
    if [[ "$CURR_FILE" == "$ALL_FILES" ]]; then
        MATCH_FILE="$ALL_FILES" # We found our current file entry
    fi
done

# If $NEXT_FILE empty we hit end of list so use First file name
if [[ "$NEXT_FILE" == "" ]]; then
    NEXT_FILE="$FIRST_FILE"
fi

echo "First file:   $FIRST_FILE"
echo "Match file:   $MATCH_FILE"
echo "Next file:    $NEXT_FILE"

# replace background file name in grub source file
sed -i "s|$CURR_FILE|$NEXT_FILE|g" /etc/default/grub

# replace background file name in grub control file
# Backup... just in case :)
cp /boot/grub/grub.cfg /boot/grub/grub.cfg~
# Short cut so we don't have to run `sudo update-grub`
sed -i "s|$CURR_FILE|$NEXT_FILE|g" /boot/grub/grub.cfg

# Now next reboot will have new background image
exit 0

A few notes

The long version answer uses a different picture directory name than the short version. In either case you need to update to whatever directory your images are stored in.

sed usually uses / as a delimiter however our path/file names contain / so we use | instead.

The conventional method after changing /etc/default/grub is to run sudo update-grub. However this takes 12 seconds on my machine the machine could be rebooted while this lengthy process was being run. Given that systemd-inhibit would have to be used.

The short cut uses sed to search and replace within /boot/grub/grub.cfg. A backup of the file is made just in case something goes wrong. The way cron reboot is setup on this machine however the process is completed by the time the login password is typed and a terminal window is opened to check on the update.

| improve this answer | |
  • This is complex, but it doesn't require renaming the wallpaper files. – Zeiss Ikon Feb 20 '17 at 12:06
  • 1
    I put a smaller version of the answer (~40% smaller) at the top. It's without echo's and variable declarations plus a smaller if-then-else-fi test. I kept the longer version there for those that wish to to debugging. – WinEunuuchs2Unix Feb 20 '17 at 17:50
  • It would probably be even easier, if used with grub-customizer, I'll try it. – Quidam Apr 21 at 18:49
  • @Quidam keep in mind it's yet another 3rd party app and comes with risks of bugs. Let us know how it turns out please. – WinEunuuchs2Unix Apr 21 at 23:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.