Hi Silvan, On Tue, Dec 01, 2015 at 08:04:04PM +0100, Silvan Jegen wrote: > Do you know if your > script works with the BSD awk implementation?
Yes, it should: I am trying to use only *nawk* features; nawk shipped first in 1987; by now every unix under the sun should have it (and most simply call it 'awk'; plus, if gawk is available, awk will be a symlink to gawk anyway) > > awk -F$'\t' '!x[$1]++' "$HISTORY" "$cache" > > For some reason this does not work for me using gawk. I get both cache > and history results (deduplicated) but the history results are not split > on tab for some reason. Probably your shell is not bash: I think the $'\t' construct is a "bashism", sorry for that. You can insert instead as an argument to -F a literal tab character (^I), but that is not easily visible in text (or worse, might be expanded by your editor into spaces). A better and guaranteed portable way is to move the definition of the field separator directly into the awk script. With that the command becomes: awk 'BEGIN { FS="\t" } !x[$1]++' "$HISTORY" "$cache" However, that still uses the initial format for the history file: "$cmdname\t$count" and we are going to swap that ... > I like your solution using awk better and would vote for putting a > version of your patch on suckless.org after adjusting it to deal with > the history file in "$count\t$cmdname" format. Do you agree? I agree. Now swapping places for the count would complicate a bit the awk one-liner I was talking about above and it would need to be like this: awk 'NR==FNR { sub("^[0-9]+\t","") } !x[$0]++' "$HISTORY" "$cache" The 'NR==FNR' test is true only for the first file (the history) and the sub will cut the count and the tab in the front of the history lines, leaving just the name; then it's the same de-duplication for all of them. And because I don't care about fields anymore, I don't even set the FS. On Wed, Dec 02, 2015 at 09:05:57AM +0100, Silvan Jegen wrote: > On Tue, Dec 1, 2015 at 8:04 PM, Silvan Jegen <s.je...@gmail.com> wrote: > > > > Personally I don't use multiselect so I will let others deal with that > > use case... > > Maybe I care a bit after all... > > Using something like > > echo -e "echo 1\nsleep 5; echo awesome\necho 200" | while read cmd; do > ${SHELL:-"/bin/sh"} -c "$cmd" & done > > would start each program after after a newline has been read. Yep, that's exactly what I was thinking. Another way is to append an '&' after all names except the last one before sending them into the $SHELL. > I don't > know how multiselect is implemented but it should be easily possible > to send the program name with newline after Ctrl-Return has been > pressed. Actually that's how multisel works right now: a Ctrl-Return just outputs that entry name (as if you'd pressed Return), but instead of exiting, dmenu highlights the entry and keeps going. It's even possible to hit Ctrl-Return repeatedly on the same entry and it will be outputted every time, so you can get it multiple times ... For dmenu's bread and butter function -- selecting programs to run -- that multisel behaviour is of questionable usefulness. However, it can be very valuable when used in some special menu applications. On Tue, Dec 01, 2015 at 08:04:04PM +0100, Silvan Jegen wrote: > The patch did not apply for me, saying that updhist.awk does not exist. > I think something went wrong with the patch creation because the diff > mentions an existing (empty?) updhist.awk file. See below. > > [...] > > This seems odd because it implies there already being an updhist.awk. I > assume that's why the patch did not apply for me. > Yeah, sorry for that. I was still trying to figure out how to coerce git to generate a diff _without_ staging the new file. (I assume the 'git apply' was the one that complained for you ? Just use the 'patch' program to apply the diff directly without git's involvement, that works for me) Anyway, based on the change in the history format, I reworked all the modifications and a new diff is attached -- this time there is no new file: there is no separate awk file, I've just inserted it as a literal awk program in the dmenu_run shell script. Other changes: - making a history file optional; the trigger to use history is a shell variable being present and non-null: DMENU_RUN_USE_HISTORY: * if its value is "DEFAULT", it will use a default location/name for the history, that is in ~/.cache or $HOME; in both cases the default name for the history file will mimic the one for the dmenu_run cache * otherwise, the variable's value will be used as the history file path; so make sure it exists, it's writable, etc. * if that variable is not set or empty, no history is used: dmenu_run will behave as it does now - added your change to deal with multisel (the while loop) The new patch attached. Give it a try.
diff --git a/dmenu_path b/dmenu_path old mode 100644 new mode 100755 index 338bac4..3f168aa --- a/dmenu_path +++ b/dmenu_path @@ -1,4 +1,10 @@ #!/bin/sh +HISTORY="$1" + +if stest -v -qfrw "$HISTORY"; then + unset HISTORY +fi + cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"} if [ -d "$cachedir" ]; then cache=$cachedir/dmenu_run @@ -7,7 +13,6 @@ else fi IFS=: if stest -dqr -n "$cache" $PATH; then - stest -flx $PATH | sort -u | tee "$cache" -else - cat "$cache" + stest -flx $PATH | sort -u > "$cache" fi +awk 'NR==FNR { sub("^[0-9]+\t","") } !x[$0]++' "$HISTORY" "$cache" diff --git a/dmenu_run b/dmenu_run index 834ede5..444aeb0 100755 --- a/dmenu_run +++ b/dmenu_run @@ -1,2 +1,43 @@ #!/bin/sh -dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & + +historyfile="$DMENU_RUN_USE_HISTORY" + +if [ -n "$historyfile" ]; then + if [ "$historyfile" = "DEFAULT" ]; then + cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"} + if [ -d "$cachedir" ]; then + historyfile=$cachedir/dmenu_history + else + historyfile=$HOME/.dmenu_history # if no xdg dir, fall back to dotfile in ~ + fi + fi +fi + +dmenu_path $historyfile | dmenu "$@" \ + | awk -v histfile=$historyfile ' + BEGIN { + FS=OFS="\t" + if(!histfile) + exit + while ( (getline < histfile) > 0 ) { + count=$1 + sub("^[0-9]+\t","") + fname=$0 + history[fname]=count + } + close(histfile) + } + + { + history[$0]++ + print + } + + END { + if(!histfile) + exit + for (f in history) + print history[f],f | "sort -t '\t' -k1rn >" histfile + } + ' \ + | while read cmd; do ${SHELL:-"/bin/sh"} -c "$cmd" & done