Scripting a fancy chooser for recently used files
By Bob Mesibov, published 17/08/2015 in Tutorials
I recently scripted a GUI dialog that lists my 10 most recently modified files in reverse chronological order and allows me to choose more than 1 file for opening. The dialog is launched with a keyboard shortcut and is shown here with 2 files selected:
The script is in copy-and-paste form at the end of this article. To explain how it works, I'll refer to the line numbers in this screenshot:
I use the find command, naturally enough, to find the most recently modified files in my home directory, as shown in line 3. find looks in the home directory (~) for regular files (type -f) modified in the last two 24-hour periods (-mtime -2). It excludes (!) any hidden files or directories starting with a '.' and also any files in my 'Mail' directory, which holds raw email client (Sylpheed) stuff (I keep my email messages in plain text files, separately).
The list of recently modified files is piped to xargs (line 4), which sends each file to the ls command with options to output in 'long listing format' (-l) and the ISO 8601 date/time format (--time-style=long-iso). Here's what those options show for each file (looking here at a subset of the files, to save space):
The bits I want from the ls output are the 6th field (date), 7th field (time) and 8th field (full path to file), so I extract them from each result with an AWK command (line 5).
The modification date is now the 1st field and the modification time the 2nd field, so I sort date and time in reverse chronological order with a sort command on these fields (line 6), then get the 10 most recently modified files with the head command (line 7). I like using tab-separated values, so I convert the spaces in the output to tabs with the tr command (line 8). (NOTE, none of the files on my system have spaces in their filenames.)
The formatted list of 10 most recently modified files is saved as the temp file /tmp/raw (line 8). I extract the filename from each full path in this file by telling AWK that the field separator in each line is a forward slash (-F/ in line 10) and asking AWK to print the last field ($NF) in each line. The extracted filenames are then joined to each line in the temp file with the paste command (line 10) with a tab as default separator, and the result is saved to a new temp file, /tmp/table10:
The table /tmp/table10 is now the working file for the chooser dialog, which I build with YAD. I want the YAD dialog to display each filename in bold, and under each filename the modification date and time in green (no special reason, just suits my color sense). To do this I tweak the text of each line in /tmp/table10 with AWK, adding the old-fashioned HTML markup for bold font (<b>) and a newline (line 12), and this time telling AWK that the field separator is a tab (-F"\t"). When I pipe the marked-up lines to YAD, YAD reads the markup as Pango markup and displays the text the way I want (see first illustration, above).
The other YAD options I use (lines 13 and 14) set the size and position of the dialog, its type (list) and the output separator (none), and allow me to make multiple selections from the list (--multiple). YAD also allows me to set where the ellipsis (...) goes in filenames too long for the width of the dialog (see 6th file in list in first illustration, above). Note that YAD only outputs the selected line in the dialog (the filenames), not the entire line as piped from AWK.
The selections I make are saved in the variable 'choices' (line 12). If I decide to cancel or close the YAD window, the script deletes my temp files and exits (lines 16 and 17). If I OK the selections, 'choices' is echoed (line 19) to sed to remove the bolding markup from the filename (line 20).
In line 21 I again use AWK, this time in a while loop. As each line with its selected filename is read by BASH, AWK looks for that filename in the first field in /tmp/table10. When it finds the line with that filename, AWK prints the full path to that file, which is the 4th field in the line (see above). For each selected filename, the full path is now passed to xdg-open, which opens the file with the default application for that file type.
The script then deletes the temp files and exits, leaving me with the selected files open on my desktop.
A strange glitch
I've had one problem with the script so far, which is that text files only open in Gedit, my default text editor, if Gedit is already running. If Gedit is closed, nothing happens when a text file is selected. If Gedit is open, multiple text file selections all open quickly in separate Gedit tabs. A similar problem has been discussed on the Fedora forum.
If you use this script, please note that it would need to be modified for filenames with spaces in them.
The script
#! /bin/bash
find ~ ! -path $HOME/'\.*' -and ! -path $HOME/'Mail*' -type f -mtime -2 \
| xargs ls -l --time-style=long-iso \
| awk '{print $6,$7,$8}' \
| sort -rk1,1 -rk2,2 \
| head \
| tr ' ' '\t' > /tmp/raw
paste <(awk -F/ '{print $NF}' /tmp/raw) /tmp/raw > /tmp/table10
choices=$(awk -F"\t" '{print "<b>"$1"</b>\n<span color=\"green\">"$2" — "$3"</span>"}' /tmp/table10 \
| yad --list --multiple --column=File --separator="" --width=300 --height=600 --center \
--ellipsize=middle --title="Recent Files")
if [[ -z $choices ]]; then
rm /tmp/raw /tmp/table10 && exit 0
else
echo "$choices" \
| sed 's/<b>\(.*\)<\/b>/\1/' \
| while read line; do xdg-open $(awk -v OPEN="$line" '$1 ~ OPEN {print $4}' /tmp/table10); done
fi
rm /tmp/raw /tmp/table10
exit 0