You are not logged in.
I also tried just installing libxml2-utils from unstable, but still received the "invalid output' error.
If you run the script in a terminal it willmost likely tell you what's up.
Offline
@ohnonot you tested this on Arch? Problem with xmllint on Debian Buster is that libxml2-utils up to 2.9.9 outputs all the values on one line. I think 2.9.10 on Bullseye will add linebreaks so mapfile will work.
You're right.
That's madly inconsistent.
I was never 100% sure about xmllint, but since it's often already installed I used it.
But this - effit, let's use xmlstarlet instead.
Also, the icon theme paths are currently hardcoded in the script (anybody using this on their own system will need to adapt this; I'll likely make it more dynamic in the future though), and 'find' will barf when it doesn't find them, which in turn makes openbox fail with "invalid output".
I added a simple "2>/dev/null" to the 'find' command, which seems to fix it, although openbox only uses stdout output for pipemenus, not stderr. Weird.
Last edited by ohnonot (2021-07-04 09:24:22)
Offline
johnraff wrote:@ohnonot you tested this on Arch? Problem with xmllint on Debian Buster is that libxml2-utils up to 2.9.9 outputs all the values on one line. I think 2.9.10 on Bullseye will add linebreaks so mapfile will work.
You're right.
That's madly inconsistent.
I was never 100% sure about xmllint, but since it's often already installed I used it.
But this - effit, let's use xmlstarlet instead.Also, the icon theme paths are currently hardcoded in the script (anybody using this on their own system will need to adapt this; I'll likely make it more dynamic in the future though), and 'find' will barf when it doesn't find them, which in turn makes openbox fail with "invalid output".
I added a simple "2>/dev/null" to the 'find' command, which seems to fix it, although openbox only uses stdout output for pipemenus, not stderr. Weird.Anyhow, I added the script to my stuff repository now.
Installed xmlstarlet and pngquant - Getting:
/pipe-recent-files: line 54: type: file: not found
also added a line to the top of the icon list just in case:
"/usr/share/icons/gnome/${isize}x${isize}/mimetypes"
(hopefully this is correct ^ )
*edit - Also tried my direct line "/usr/share/icons/gnome/32x32/apps/accessories-text-editor.png\" removing the other lines just to check.
Last edited by sleekmason (2021-02-18 20:25:04)
Offline
/pipe-recent-files: line 54: type: file: not found
That means that the "file" program is not installed.
There's also a list of dependencies at the top of the script.
"apt install file".
also added a line to the top of the icon list just in case:
"/usr/share/icons/gnome/${isize}x${isize}/mimetypes"
(hopefully this is correct ^ )
This is correct.
*edit - Also tried my direct line "/usr/share/icons/gnome/32x32/apps/accessories-text-editor.png\" removing the other lines just to check.
for iconthemepath? That would be incorrect.
I will now add even more comments to the script, make sure you grab the newest version.
Last edited by ohnonot (2021-02-19 03:21:35)
Offline
johnraff wrote:libxml2-utils up to 2.9.9 outputs all the values on one line. I think 2.9.10 on Bullseye will add linebreaks
You're right.
That's madly inconsistent.
I was never 100% sure about xmllint, but since it's often already installed I used it.
But this - effit, let's use xmlstarlet instead.
xmllint seems to be on most systems by default, so in cases where it would do the job it would be a good choice.
From Debian Bullseye the linebreaks go in:
https://gitlab.gnome.org/GNOME/libxml2/ … c1c2cf1bce
As they say, it's a "breaking change" but the previous behaviour made the output almost unusable, so probably few people complained.
I ran 'time' on the three options 'xmllint', 'xpath' and 'xmlstarlet' and for some reason xpath was much slower than the other two, by a factor of 10. Xmlstarlet is very powerful and versatile - I'm using it in a snippet I wrote years ago, but am now fired up to play with it some more...
---
...get better mimetype (although this info is present in recentfile, but it's mostly garbage)
There is a possibility of improving the script here, though it would take quite a bit more work probably. Ignoring the mime-type entry in recently-used.xbel and just relying on xdg-open to open the file is quite a reasonable choice usually, but there are times when a file could usefully be opened by two quite different applications - think html files and browser vs text editor. In such cases it's useful to know what app the user actually last opened the file with, and that data is also in the xbel file.
My script (it wasn't written by Corenominal) parsed that out along with the file path, but I used an embedded awk section. (I was into awk at the time.) It would probably be quite possible to do with xmlstarlet + shell commands, but I don't feel like tackling it myself now: BL uses @twoion's lua script which works fine - as did my bash+awk script - except for the lack of icons. The icons are a definite plus though, for menus which are using icons elsewhere.
...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )
Offline
recent wrote:...get better mimetype (although this info is present in recentfile, but it's mostly garbage)
There is a possibility of improving the script here, though it would take quite a bit more work probably. Ignoring the mime-type entry in recently-used.xbel and just relying on xdg-open to open the file is quite a reasonable choice usually, but there are times when a file could usefully be opened by two quite different applications - think html files and browser vs text editor. In such cases it's useful to know what app the user actually last opened the file with, and that data is also in the xbel file.
My script just uses xdg-open anyhow (another dependency I forgot to mention), and that's fine with me, but it wants to know the proper filetype to associate a meaningful icon (see screenshot).
recently-used.xbel seems to assign "application/octet-stream" to all executable scripts, which a) isn't very helpful and b) doesn't correspond to what my filemanager shows me (i.e. a shellscript icon) and c) there's no '*octet-stream*' icon on my system anywhere.
I even tried to figure out what process creates recently-used.xbel, maybe I could configure that instead... meanwhile 'file' will have to do - and because it can identify many files in one go, the overhead is minimal.
Offline
sleekmason wrote:/pipe-recent-files: line 54: type: file: not found
That means that the "file" program is not installed.
There's also a list of dependencies at the top of the script.
"apt install file".
Yes, of course. To make matters worse, I watched it uninstall not long before that while installing another program.
Still receiving the Openbox error.
Made sure xmlstarlet, coreutils, file, find, xdg-utils, pngquant, and graphicsmagick-imagemagick-compat (This was the package indicated for convert). <- Hopefully correct?
Installed the papirus icons as well for good measure just in case.
Now in a terminal I get:
sleekmason@ai:~$ pipe-recent-files
<openbox_pipe_menu>
Last edited by sleekmason (2021-02-19 21:30:03)
Offline
My script just uses xdg-open anyhow...
As I said, a perfectly reasonable choice, I think. Its exact behaviour varies from system to system, though, because it hands over the job to some DE "*-open" type tool if it can find one. On BL it will call 'exo-open' because we set XDG_CURRENT_DESKTOP to 'xfce' (this could be up for reconsidering IMO).
...but it wants to know the proper filetype to associate a meaningful icon (see screenshot).
Does xdg-open assign an icon when opening a file? Or are you talking about the 'recents' script itself at this point?
...recently-used.xbel seems to assign "application/octet-stream" to all executable scripts
I just checked and saw 'application/x-shellscript' for an executable shell script, so this also seems to vary between systems. Maybe it's up to the application which last opened the file to add this entry?
I even tried to figure out what process creates recently-used.xbel...
Indeed. That would be interesting to know.
...'file' will have to do...
File seems quite usable. Another alternative is xdg-mime which comes with xdg-open though I haven't compared it.
---
But my point in the last post was about a possible alternative way of choosing an application to open the file - leaving the (impressive) icon mechanism you created in place. The xbel file records the last app to open each file (sometimes a list of two or three) in <bookmark:application... tags and the last two BL & CB scripts parsed out one of those to open the file. Sometimes that's what the user wants, rather than the preferred app derived from the mime-type + mimeapps.list (as with whether to open an html file in a browser or editor). It's not foolproof though. I've got a couple of dead entries in my current menu, which ought to be debugged...
...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )
Offline
Oh, xdg-mime can also do this. Interesting, I'll have to check that out.
It's a shell script itself, so chances are good I can figure the mechanism out.
I even tried to figure out what process creates recently-used.xbel...
Indeed. That would be interesting to know.
It's the application that first opens the file I guess.
Try clearing your recntly-used.xbel, then open an executable shell script straight from geany (not from the filemanager). Does it show up as "application/octet-stream" now?
I think there's something wrong with geany in that respect.
But my point in the last post was about a possible alternative way of choosing an application to open the file - leaving the (impressive) icon mechanism you created in place. The xbel file records the last app to open each file (sometimes a list of two or three) in <bookmark:application... tags and the last two BL & CB scripts parsed out one of those to open the file. Sometimes that's what the user wants, rather than the preferred app derived from the mime-type + mimeapps.list (as with whether to open an html file in a browser or editor).
Yes, that makes sense, and I think I will amend that.
It's not foolproof though. I've got a couple of dead entries in my current menu, which ought to be debugged...
My script drops non-readable (non-existing) files.
Also, other people have created openbox-recent pipemenus, I should research before re-inventing.
Offline
graphicsmagick-imagemagick-compat (This was the package indicated for convert). <- Hopefully correct?
As long as convert works as required:
convert -background none someicon.svg someicon.png
---
Now in a terminal I get:
sleekmason@ai:~$ pipe-recent-files <openbox_pipe_menu>
That's all? Weird.
Did you pull the newest version?
Do that, then if you still don't get more, try adding "set -x" to the top of the script.
Last edited by ohnonot (2021-02-20 05:01:30)
Offline
Oh, xdg-mime can also do this. Interesting, I'll have to check that out.
It's a shell script itself, so chances are good I can figure the mechanism out.
I've just been looking some more at xdg-open and xdg-mime. xdg-open in fact calls xdg-mime in its "generic" mode (no desktop detected) but xdg-mime looks buggy to me. The info_gnome() function - called if it detects an XFCE desktop - exits with success even if none of gio, gvfs-info or gnomevs-info are present.
( Ah, it's a known bug: https://bugs.debian.org/cgi-bin/bugrepo … bug=838120 )
info_generic() looks for mimetype and falls back to file - so we're back where we started...
There is 'mimetype' from libfile-mimeinfo-perl which seems more complicated (and slower) than 'file --mime-type' but I haven't reasearched enough to have an opinion on which is better.
Try clearing your recntly-used.xbel, then open an executable shell script straight from geany (not from the filemanager). Does it show up as "application/octet-stream" now?
No, it's still "application/x-shellscript"
johnraff wrote:It's not foolproof though. I've got a couple of dead entries in my current menu, which ought to be debugged...
My script drops non-readable (non-existing) files.
It's not because the file is unreadable. It's a problem with the BL script dropping a needed option in one case (meld). In another case, if Thunderbird saves an attachment as a .png file which I haven't yet opened in an image viewer, then the only application stored in *.xbel is Thunderbird, so clicking the menu item sends it to Tbird which offers to send it as an attachment in an email. Fair enough, but perhaps not what most users would want.
Maybe the simple mime-type based approach is the best...
Also, other people have created openbox-recent pipemenus, I should research before re-inventing.
I haven't yet found any that weren't based on the (somewhat hacky) shell script in the OB wiki. If you find one, please post a link!
...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )
Offline
Now in a terminal I get:
sleekmason@ai:~$ pipe-recent-files <openbox_pipe_menu>
Me too.
Line 61:
[ -r "$recentfile" ] || echo "<item label=\"No data to process\" /></openbox_pipe_menu>" && exit 1
This exits right away. That possible error usually gets picked up by shellcheck, but not this time for some reason. Anyway, this allows the script to run:
[ -r "$recentfile" ] || { echo "<item label=\"No data to process\" /></openbox_pipe_menu>" && exit 1 ;}
---
Now I've looked at the code, this rearrangement makes debugging easier, from line 111:
# finally print out this recent item
echo "<item label=\"$label\" icon=\"$icon\">
<action name=\"Execute\">
<command>
<![CDATA[xdg-open '${file[i]}']]>
</command>
</action>
</item>"
---
And I found that issue with meld that our lua script also suffers from. After doing a file comparison, meld puts a recent-********.meldcmp file (in ~/.local/share/meld/) which appears in the recent files menu. Meld can redo the comparison if run as 'meld --comparison-file <filepath>' and that command is written into recently-used.xbel like this:
<bookmark:application name="Meld" exec="'/usr/bin/meld --comparison-file %u'" modified="2021-02-20T07:51:20Z" count="2"/>
Running 'xdg-open <filepath>' doesn't work. (My script gets it. O:) )
...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )
Offline
Try clearing your recntly-used.xbel, then open an executable shell script straight from geany (not from the filemanager). Does it show up as "application/octet-stream" now?
No, it's still "application/x-shellscript"
OK, then my geany is different from yours. I wonder why. I'm on version 1.37.1.
"application/octet-stream" seems to be a fancy way of saying "I have no clue what this file is".
---
Now in a terminal I get:
sleekmason@ai:~$ pipe-recent-files <openbox_pipe_menu>
Sorry for that, I must have pushed before saving the file.
It should be working now.
---
I almost completely rewrote the script.
Openbox can use SVG graphics, yes?
In that case there's no need to convert anything - the previous 'mkicon()' function is now 'findicon()' and simply returns a suitable mimetype icon with some clever file system traversing (no 'find').
There's also no need to provide icon theme paths (although you still can), 'findicon()' finds the current theme, reads the Inherits, and searches there.
All this is happening so fast that no caching is required.
[ I also added a function that replaces the stupid octet-stream mimetype with something more useful, as discovered by 'gio' (part of glib2, or rather libglib2.0-bin on Debian). If you don't have that problem you might not need it. In any case, 'gio' is an optional dependency and installing libglib2.0-bin might fix weird icons. ]
The script now almost completely gets its information from recently-used.xbel with xmlstarlet, and sorts by recently used - the last item in the file is not always the most recently used!
It also opens the entry with the application defined in recently-used.xbel, and not 'xdg-open'.
I must say I'm very happy with the result. There's still some room for improvement of course.
But feel free to use and reuse this.
Offline
@ohnonot, Closer! Your newest script opens the menu, but without the icons.
The line johnraff provided made the earlier version work as expected(afaik).
*Edit The 'clear recents' icon is there.
*Edit 2- The icon for "downloads" is "unknown" in the first script. Everything else looks groovy.
Last edited by sleekmason (2021-02-20 14:25:42)
Offline
What exactly is "downloads"? AFAIK recently-used.xbel does not store folders, only files.
So beyond that, all icons are good?
If there's any sort of problem with icons, you need to look at your icon theme's index.theme - sometimes the Inherits are messed up or plain missing, in that case you can provide a chain of icon themes to search on the command line.
If you think it will help me debug, you can always provide the output of the script.
The '-d' option will give a little additional info.
Offline
What exactly is "downloads"? AFAIK recently-used.xbel does not store folders, only files.
So beyond that, all icons are good?If there's any sort of problem with icons, you need to look at your icon theme's index.theme - sometimes the Inherits are messed up or plain missing, in that case you can provide a chain of icon themes to search on the command line.
If you think it will help me debug, you can always provide the output of the script.
The '-d' option will give a little additional info.
Super sorry about that. I made a mistake. Not downloads, but ISO files in downloads.
Matters not a bit, and easy enough to change if need be. Also couldn't reproduce!
Really digging the separate icons. Well done:)
Edit* Everything is Awesome:) Forgot to change themes! <-- then looked at the script and saw it shouldn't be necessary to add gnome anymore. - See below.
Edit 2* Really fast as well.
edit 3* Ha! Very nice:) just saying.
Edit 4* Found an issue. - None of the "named" Gnome Colors work. Gnome-brave, gnome-wise etc.. Gnome, and Papirus work wonderfully.
Heres -d while in gnome-brave:
sleekmason@ai:~$ pipe-recent-files -d
<openbox_pipe_menu>
/usr/local/bin/pipe-recent-files: line 165: type: gio: not found
0 /home/sleekmason/Pictures/wallpapers/Ice.jpg
0 jpeg
0 firefox-esr "/home/sleekmason/Pictures/wallpapers/Ice.jpg"
1 /home/sleekmason/Pictures/wallpapers/IMG_20210220_112137.jpg
1 jpeg
1 thunderbird "/home/sleekmason/Pictures/wallpapers/IMG_20210220_112137.jpg"
2 /home/sleekmason/Bin/gpg
2 x-shellscript
2 geany "/home/sleekmason/Bin/gpg"
3 /home/sleekmason/packages_list.txt
3 plain
3 mousepad "/home/sleekmason/packages_list.txt"
########################
########################
Icon lookup @ /usr/share/icons/gnome-brave
Icon lookup @ /usr/share/icons/gnome-colors-common
Icon lookup @ /usr/share/icons/gnome-brave
Icon lookup @ /usr/share/icons/gnome-colors-common
<item label="~/Pictures/wallpapers/Ice.jpg " icon=""><action name="Execute"><command><![CDATA[firefox-esr "/home/sleekmason/Pictures/wallpapers/Ice.jpg"]]></command></action></item>
Icon lookup @ /usr/share/icons/gnome-brave
Icon lookup @ /usr/share/icons/gnome-colors-common
Icon lookup @ /usr/share/icons/gnome-brave
Icon lookup @ /usr/share/icons/gnome-colors-common
<item label="~/Pictures/wallpapers/IMG__20210220__112137.jpg " icon=""><action name="Execute"><command><![CDATA[thunderbird "/home/sleekmason/Pictures/wallpapers/IMG_20210220_112137.jpg"]]></command></action></item>
Icon lookup @ /usr/share/icons/gnome-brave
Icon lookup @ /usr/share/icons/gnome-colors-common
Icon lookup @ /usr/share/icons/gnome-brave
Icon lookup @ /usr/share/icons/gnome-colors-common
<item label="~/Bin/gpg " icon=""><action name="Execute"><command><![CDATA[geany "/home/sleekmason/Bin/gpg"]]></command></action></item>
Icon lookup @ /usr/share/icons/gnome-brave
Icon lookup @ /usr/share/icons/gnome-colors-common
Icon lookup @ /usr/share/icons/gnome-brave
Icon lookup @ /usr/share/icons/gnome-colors-common
<item label="~/packages__list.txt " icon=""><action name="Execute"><command><![CDATA[mousepad "/home/sleekmason/packages_list.txt"]]></command></action></item>
Icon lookup @ /usr/share/icons/gnome-brave
Icon lookup @ /usr/share/icons/gnome-colors-common
<separator /><item icon="/usr/share/icons/gnome-colors-common/22x22/actions/edit-delete.png" label="Clear Recents"><action name="Execute"><prompt>Really delete ~/.local/share/recently-used.xbel?</prompt><command><![CDATA["/usr/local/bin/pipe-recent-files" -c]]></command></action></item></openbox_pipe_menu>
sleekmason@ai:~$
Here is the same with epapirus activated;
sleekmason@ai:~$ pipe-recent-files -d
<openbox_pipe_menu>
/usr/local/bin/pipe-recent-files: line 165: type: gio: not found
0 /home/sleekmason/Pictures/wallpapers/Ice.jpg
0 jpeg
0 firefox-esr "/home/sleekmason/Pictures/wallpapers/Ice.jpg"
1 /home/sleekmason/Pictures/wallpapers/IMG_20210220_112137.jpg
1 jpeg
1 thunderbird "/home/sleekmason/Pictures/wallpapers/IMG_20210220_112137.jpg"
2 /home/sleekmason/Bin/gpg
2 x-shellscript
2 geany "/home/sleekmason/Bin/gpg"
3 /home/sleekmason/packages_list.txt
3 plain
3 mousepad "/home/sleekmason/packages_list.txt"
########################
########################
Icon lookup @ /usr/share/icons/ePapirus
<item label="~/Pictures/wallpapers/Ice.jpg " icon="/usr/share/icons/ePapirus/22x22/mimetypes/image-jpeg.svg"><action name="Execute"><command><![CDATA[firefox-esr "/home/sleekmason/Pictures/wallpapers/Ice.jpg"]]></command></action></item>
Icon lookup @ /usr/share/icons/ePapirus
<item label="~/Pictures/wallpapers/IMG__20210220__112137.jpg " icon="/usr/share/icons/ePapirus/22x22/mimetypes/image-jpeg.svg"><action name="Execute"><command><![CDATA[thunderbird "/home/sleekmason/Pictures/wallpapers/IMG_20210220_112137.jpg"]]></command></action></item>
Icon lookup @ /usr/share/icons/ePapirus
<item label="~/Bin/gpg " icon="/usr/share/icons/ePapirus/22x22/mimetypes/application-x-shellscript.svg"><action name="Execute"><command><![CDATA[geany "/home/sleekmason/Bin/gpg"]]></command></action></item>
Icon lookup @ /usr/share/icons/ePapirus
<item label="~/packages__list.txt " icon="/usr/share/icons/ePapirus/22x22/mimetypes/text-plain.svg"><action name="Execute"><command><![CDATA[mousepad "/home/sleekmason/packages_list.txt"]]></command></action></item>
Icon lookup @ /usr/share/icons/ePapirus
<separator /><item icon="/usr/share/icons/ePapirus/22x22/actions/delete.svg" label="Clear Recents"><action name="Execute"><prompt>Really delete ~/.local/share/recently-used.xbel?</prompt><command><![CDATA["/usr/local/bin/pipe-recent-files" -c]]></command></action></item></openbox_pipe_menu>
sleekmason@ai:~$
Last edited by sleekmason (2021-02-20 22:50:09)
Offline
Edit 4* Found an issue. - None of the "named" Gnome Colors work. Gnome-brave, gnome-wise etc.. Gnome, and Papirus work wonderfully.
Thank you for the output.
I think you got bit by this:
If there's any sort of problem with icons, you need to look at your icon theme's index.theme - sometimes the Inherits are messed up or plain missing, in that case you can provide a chain of icon themes to search on the command line.
So that would be /usr/share/icons/gnome-*/index.theme I guess.
This is often a problem with the icon themes themselves and how they're packaged - the Inherits should be dependencies but aren't. In other cases the designers seem to be suffering delusions of grandeur when they don't supply any Inherits at all, supposedly thinking their theme is perfect.
So it's not the recent script's "fault", but I provided a fallback mechanism just for such cases. Read the code.
Offline
johnraff wrote:Try clearing your recntly-used.xbel, then open an executable shell script straight from geany (not from the filemanager). Does it show up as "application/octet-stream" now?
No, it's still "application/x-shellscript"
OK, then my geany is different from yours. I wonder why. I'm on version 1.37.1.
"application/octet-stream" seems to be a fancy way of saying "I have no clue what this file is".
On Buster, I've got geany 1.33. I do get the octet mimetypes sometimes, but not as often as you seem to. "No clue" seems to be about right though - instead of trying to cope, you could just use some generic icon in those cases?
Xmlstarlet is very powerful and versatile - I'm using it in a snippet I wrote years ago, but am now fired up to play with it some more...
I almost completely rewrote the script.
...
The script now almost completely gets its information from recently-used.xbel with xmlstarlet, and sorts by recently used - the last item in the file is not always the most recently used!
It also opens the entry with the application defined in recently-used.xbel, and not 'xdg-open'.
While I was playing around with xmlstarlet you totally GOT it.
That's the perfect invocation to extract the info needed. Great job.
I'll stash that snippet of code away for further use.
---
I've been running your script and do have a few suggestions though - of course it's up to you whether to apply any of them...
Small stuff first:
*) A -h/--help option?
*) An option to display the filename only, not the full path?
*) An option not to display icons? I know icons are a major feature here, but I think it's a solid recent files menu without them too.
*) The icons look a bit squashed together in my menu. I'd like a bit of padding round them.
*) There is quite a lot of output on STDERR which will spam out ~/.xsession-errors with meaningless messages. Create a debug() function (turned on when $debug=1) to replace echo in those places?
*) Shellcheck on line #99: "recent-files-ohnonot.sh:99:5: warning: Invalid flags are not handled. Add a *) case. [SC2220]"
*) Grep is pretty fast, and 'read -r line file' can be slow - are you sure that G() function gives you a speed advantage?
*) In several places you chek if an array is empty like this: '[[ "$array" == "" ]] &&...' By default bash will assume you mean the first element, so it works OK, but it would feel cleaner to do [[ "${array[0]}" = "" ]] or [[ -z "${array[0]}" ]] or [[ ${#array[@]} -eq 0 ]]...
*) You go through the array of parsed xml data twice - can it not all be done in one go? I don't know if that would make any significant difference to running time, though.
*)
And the big one: escaping special characters. You only handle %20 for spaces, but there are many many more, and if any of them remain in the file path as-is then things you do later get broken. Apart from quotes, apostrophes etc there's the whole world of unicode. As it is your script can't handle files with Japanese names. (But see the tweaked version in the screenshot has one.) Instead of trying to do each one in bash (there are hundreds, if not thousands) in the past I used a perl snippet to decode everything:
var=$(perl -MURI::Escape -e 'print uri_unescape($ARGV[0]);' "$var" )
In my tweaked version of your script I applied that to each array element in turn, and because perl is so fast it doesn't make it intolerably slow, but still perceptably so. It would be more efficient to run it only once over the whole menu as a single string - maybe that could be done right at the end if you collected all the items in a variable instead of echoing them when they're generated.
I hit some other fiddly issues with quoting and escaping for xml.
I've got a folder full of files with difficult names for testing menus like this. One such file is
/home/john/text/oddnames/"new folder"/It's called "conkyrc-caption", "so?"
Yes that's its name, and the regular tools like thunar and geany can all handle it. For your script to do the same I had to escape some xml special characters, and escape single quotes as '\'', otherwise some files don't appear in the menu, or don't open correctly when clicked.
Here's the tweaked version. (This contains none of the above smaller suggestions, just the quoting/escaping.) All I can say is that it works, but it could easily do with some optimization.
#!/bin/bash
# Openbox pipemenu to create a list of recent files with icons
# (if you don't care about icons this will be a waste of resources)
# also see: forums.bunsenlabs.org/viewtopic.php?pid=111438#p111438
### Dependencies:
# - xmlstarlet parsing recentfile
### Optional:
# - gio (glib2, libglib2.0-bin) redefine useless mimetypes
# Options:
# -r str Recentfile to parse (default: $HOME/.local/share/recently-used.xbel)
# -c Clear the recentfile
#
# -d Output a little more to stderr
# -s str Comma-separated list of icon sizes to consider. Default 22,24,32
# -t str Comma-separated list of Icon theme names to consider. No default.
# Example: "Tela,Papirus,Paper"
# -m int maximum number of results to display
# If the user didn't define iconthemes, we extract the current one from
# gtk3's settings.ini and append the "Inherits" found in that theme's index.theme.
# Warning: many icon themes have messy Inherits - if the result is less than
# desirable, one can override the whole Icon theme chain with the '-' option.
max=20 # maximum number of recent files
# Comma-separated list of Desired icon sizes.
# Choose something that is actually found in icon themes.
isizes=22,24,32
debug=0
recentfile="$HOME/.local/share/recently-used.xbel"
gtksettingsfile="${XDG_CONFIG_HOME-"$HOME/.config"}/gtk-3.0/settings.ini"
G() {
# simplistic grep replacement
# only works on files, only the first match is assigned to a variable
# $1: query
# $2: file
# $3: variable to use
while read -r line; do
[[ "$line" == *"$1"* ]] && declare -g "$3"="$line" && return
done <"$2"
}
# This function will return exactly one icon for a mimetype
findicon() {
mime="$1"
queries="${2-"mimetypes,mimes"}" # comma-separated list of icon type subfolders
shopt -s nullglob # required to recognize empty results
# If the user didn't define iconthemes, we extract the current one from
# gtk3's settings.ini and append the "Inherits" found in that theme's index.theme.
if [[ "$iconthemes" == "" ]]; then
G gtk-icon-theme-name "$gtksettingsfile" iconthemes
iconthemes="${iconthemes##*=}"
iconthemes="${iconthemes#*\"}"
iconthemes="${iconthemes%\"*}"
for path in "$HOME/.local/share/icons" "/usr/share/icons"; do
path="$path/$iconthemes/index.theme"
[ -r "$path" ] && break
done
G 'Inherits=' "$path" inherits
iconthemes="${iconthemes},${inherits#*=}"
fi
src_icon=""
for theme in ${iconthemes//,/ }; do
for path in "$HOME/.local/share/icons" "/usr/share/icons"; do
path="$path/$theme"
[ -d "$path" ] || continue
echo "Icon lookup @ $path" >&2
for isize in ${isizes//,/ }; do
for dir1 in "$isize" "${isize}x$isize" scalable; do
for dir2 in ${queries//,/ }; do
for dir in "$dir1/$dir2" "$dir2/$dir1"; do
[ -d "$path/$dir" ] || continue
for src_icon in "$path/$dir/$mime".???; do [ -r "$src_icon" ] && break 7; done
for src_icon in "$path/$dir/"*"-$mime".???; do [ -r "$src_icon" ] && break 7; done
done
done
done
done
done
done
# no results? OK, no icon then, and return 1 == error
[[ "$src_icon" == "" ]] && return 1
echo "$src_icon"
}
pipe_exit() {
echo "<item label=\"$*\" /></openbox_pipe_menu>"
exit 1
}
######################### MAIN ###############################
echo "<openbox_pipe_menu>"
while getopts "dcs:t:m:r:g:" opt; do
case $opt in
d) debug=1
;;
c)
cat <<EOF > "$recentfile"
<?xml version="1.0" encoding="UTF-8"?>
<xbel version="1.0"
xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks"
xmlns:mime="http://www.freedesktop.org/standards/shared-mime-info"
>
</xbel>
EOF
exit
;;
s) isizes="$OPTARG"
;;
t) # a comma-separated list of icon theme names to search for mimetype icons
# overrides automatic detection
iconthemes="$OPTARG"
;;
m) [[ "$OPTARG" =~ [0-9]+ ]] && (( OPTARG >= 0 )) && (( OPTARG <= 65535 )) || pipe_exit "Option -${opt}: invalid number $OPTARG"
max="$OPTARG"
;;
r) [ -r "$OPTARG" ] || pipe_exit "Option -${opt}: Cannot read $OPTARG"
recentfile="$OPTARG"
;;
g) [ -r "$OPTARG" ] || pipe_exit "Option -${opt}: Cannot read $OPTARG"
gtksettingsfile="$OPTARG"
;;
esac
done
# quick dependency check
for dep in xmlstarlet; do
type "$dep" >/dev/null || exit 1
done
# Use an Xpath expression to collect all
# 1) file:///...
# 2) mime-type
# 3) application
# elements, sorted by last modified date. Read into array and limit to $max elements.
mapfile -n "$((max*3))" -t array <<<"$(xmlstarlet sel -t -m "/xbel/bookmark" -s D:T:U '@modified' \
-v '@href' -n \
-v 'info/metadata/mime:mime-type/@type' -n \
-v 'info/metadata/bookmark:applications/bookmark:application[last()]/@exec' -n "$recentfile")"
[[ "$array" == "" ]] && pipe_exit "No file data to process"
mimetypestofix=()
j=0
# Converting file:/// elements to proper unix paths:
for ((i=0;i<${#array[@]};i++)); do
# file path
array[i]="${array[i]#*file://}"
#array[i]="${array[i]%\"*}"
array[i]=$(perl -MURI::Escape -e 'print uri_unescape($ARGV[0]);' "${array[i]}" )
#array[i]="${array[i]//%20/ }"
((i++))
# mime-type - keep only the part after the /
array[i]="${array[i]#*/}"
# if the mime-type is the useless octet-stream, collect files to re-test with gio:
if [[ "${array[i]}" == "octet-stream" ]]; then
mimetypestofix[$((j++))]="${array[$((i-1))]}"
fi
((i++))
# command to open file
# remove enclosing single quotes
array[i]="${array[i]#\'}"
array[i]="${array[i]%\'}"
filepath="${array[$((i-2))]}"
# escaping single quotes is tricky
filepath="${filepath//\'/\'\\\'\'}"
# replace %u with quoted file path
array[i]="${array[i]//%u/\'${filepath}\'}"
done
mimetypesfixed=()
if type gio >/dev/null && [[ "$mimetypestofix" != "" ]]; then
# We need to fix the stupid octet-stream mimetype.
# to get only the last line of the gio output for each file we use mapfile with
# a callback function, see: wiki.bash-hackers.org/commands/builtin/mapfile#the_callback
mtf() { mimetypesfixed[$(($1/5))]="${2##*/}"; }
mapfile -t -c 5 -C 'mtf ' <<<"$(gio info -a standard::content-type "${mimetypestofix[@]}")"
fi
if [[ "$debug" == 1 ]]; then
for ((i=0;i<${#array[@]};i++)); do
echo -e "$((i/3))\t${array[i]}"
done
echo "########################"
for ((i=0;i<${#mimetypestofix[@]};i++)); do
echo -e "$((i))\t${mimetypestofix[i]}"
done
echo "########################"
for ((i=0;i<${#mimetypesfixed[@]};i++)); do
echo -e "$((i))\t${mimetypesfixed[i]}"
done
fi >&2
# go through the array 3 lines at a time, and output openbox pipemenu stuff:
for ((i=0,j=0;i<${#array[@]};i+=3)); do
# 1st line: the file path
# 2nd line: the mimetype
# 3rd line: the command to execute
# 1. the file - skip if unreadable
[ -r "${array[i]}" ] || continue
array[i]="${array[i]//\'/'}"
array[i]="${array[i]//\"/"}"
array[i]="${array[i]//</<}"
array[i]="${array[i]//>/>}"
# label of the menu entry - openbox-specific
label="${array[i]/$HOME/\~}"
label="${label//_/__}"
# 2. The mimetype
type="${array[$((i+1))]}"
if [[ "$mimetypesfixed" != "" ]]; then
echo "unfixed $type" >&2
[[ "$type" == "octet-stream" ]] && type="${mimetypesfixed[$((j++))]}"
echo "fixed $type" >&2
fi
# find a suitable icon
icon="$(findicon "$type" || findicon unknown)"
# 3. the command
cmd="${array[$((i+2))]}"
# finally print out the recent item
echo "<item label=\"$label \" icon=\"$icon\">
<action name=\"Execute\">
<command>
<![CDATA[$cmd]]>
</command>
</action>
</item>"
done
# close up the pipemenu, with one last entry to remove $recentfile and a suitable "delete" icon
icon="$(findicon delete actions)"
echo "<separator /><item icon=\"$icon\" label=\"Clear Recents\"><action name=\"Execute\"><prompt>Really delete ${recentfile//$HOME/\~}?</prompt><command><![CDATA[\"$0\" -c]]></command></action></item></openbox_pipe_menu>"
Last edited by johnraff (2021-02-21 13:15:38)
...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )
Offline
^Added a couple more suggestions.
BTW as to what is writing to recently-used.xbel, I guess the most likely suspect would be a GTK library?
...elevator in the Brain Hotel, broken down but just as well...
( a boring Japan blog (currently paused), now on Bluesky, there's also some GitStuff )
Offline
Where and how to do this?
My original super duper code was 18 lines long. This is 238, and we are way past my knowledge level.
Right now, gnome-colors do not work. *Edit Nevermind, I'm an idiot. Finally figured it out.
I do not know how to fix this or I would.
I don't mind doing debugging, but please don't leave me stranded without a direct example.
Edit* Yes, You did.
I see here:
# -t str Comma-separated list of Icon theme names to consider. No default.
# Example: "Tela,Papirus,Paper"
But this does not make any sense to me. Where do I put this line in the script? How does it begin? (-t str)?
*Edit All makes sense now.
For instance, for the icon size, I can see
isizes=22,24,32
So I have at least something to reference.
Sorry if I am being dense here. I can build a house from the ground up, everything included. But I will never be able to code like you guys do. *Edit - yep being dense.
Honestly? It bothers me to no end that I can't just seem to "grab" it. It really seems simple on the face, and then "by degrees" more difficulty just a short ways in. Time consuming to learn I would think. Somehow I get the idea that just taking a class isn't going to suddenly wise me up.
Edit* ^ I just want to get to the point I don't waste hours, when all I have to do is change the concept of how I think of something.
And, You guys rock. Period.
Last edited by sleekmason (2021-02-21 22:15:20)
Offline