1

After some years of "BASHing", I've wrote several custom scripts. Most of them I don't use on a daily basis, so it's easy to forget how I've named them, as consistent as I might try to be.

So I know that if I'm intending to run my FLAC2MP3.bat, I just write FLA +TABTAB, and there I have a list of what Bash could find in my $PATH:

FLAC2MP3.bat FLAC2MP3.bat~ FlashPlayerApp.exe FlashPlayerCPLApp.cpl

Is it possible to do the same kind of searching but writing *MP3* (or anything similar) to search for any files that match the pattern on my PATH? When I do write that and press TAB (once or twice), basically nothing happens.

What I've already tried:

  • Although it's not identical to my question, I've tried the answers for "How do I let bash autocomplete wildcards?". They seem to only relate to completions on the current dir contents, which is not my case at all.
  • This answer to an almost similar question (but not involving wildcards) seemed like a good idea: using find on $PATH. Although it's not as practical as pressing TAB, it could be a solution. However, I almost got crazy and had no success with variable expansion, since my PATH (as most of Cygwin PATHs) has spaces into some directories, and expansion inserts single quotes when finding them (find \'${PATH/:/\' \'}\' -maxdepth 1 -iname mp3 -print -quit). In reality, I've lost so much time trying to make this work that it could almost be a separate question.
3
  • if you put all your own scripts in, say ~/bin/ you can run ls ~/bin/*mp3*. BTW, it's a good idea to write your scripts so that, even if you don't use getopts or getopt or similar for option processing, they all understand at least a -h and/or --help option (which displays basic usage info and exits).
    – cas
    Aug 4, 2017 at 13:07
  • What you probably need is something that can apply a glob expression to each PATH (or whatever) element. I use github.com/thrig/scripts/blob/master/filesys/findin.c to do that.
    – thrig
    Aug 4, 2017 at 15:06
  • Good hint, @thrig. I did something similar in a separate script, but was trying to see something more "TABbable". :-) But thanks, it's always good to check some good C code. Aug 9, 2017 at 3:39

3 Answers 3

2

Switch to zsh. Make sure your .zshrc includes the line

setopt glob_complete

Enjoy.

1
  • Definitely answers my question - although I admit I wasn't counting on changing shells. But... why not give it a try? Already installed it and trying it - and liking it so far. Thanks for the very helpful links. Aug 9, 2017 at 5:10
1

Would

ls `echo $PATH | sed s@ @\\ @g | sed s@:@ @g` | grep [string]

accomplish what you need, on both counts? sed s@ @\\ @g should replace $PATH entries that have spaces in them with the readable escaped space and sed s@:@ @g should replace the colons separating $PATH entries with a normal space which allows ls to individually list the contents of each directory, which can be passed into grep which recognizes regex searching like the wildcards you want to use. This works as far as I can tell in native Bash, but I'm not sure if it will work in Cygwin's Bash.

If it does work out, you can always alias it so it is less cumbersome to use.

1
  • This one put me again into the successive failed attempts of making most of the utils helping me in the task. Somehow I've managed it, but ls couldn't solve this problem for me. But I've used part of your solution: echo $PATH | sed 's@ @\\ @g' | sed 's@:@\n@g' | xargs -I{} find '{}' . -type f -maxdepth 1 -iname '*mp3*' -print' It does work, but obviously has to be used out of TAB context. Aug 9, 2017 at 6:07
1

I don't have an answer to your primary question but I got curious about your second "what I've already tried" item and so far the best I can do to get that working with a single command, no subshell is:

IFS=: find_path=(${PATH}) && find "${find_path[@]}" <your_find_params>

This is ugly and proves that single-command/no-subshell is probably not worth the effort. Per Stephane's comment below you can instead do something like

(IFS=:; set -f; find $PATH <your_find_params>)

Edit: Of course this doesn't handle the case of a newline embedded in a path component but who cares...we're living in the real world. :)

5
  • 1
    Why are you no simply using (IFS=:; set -f; find $PATH ...) (note that it won't work for empty path components (which mean the current directory and should not be used anyway) Aug 4, 2017 at 14:53
  • 1
    Why? I suppose in part it's for the same reason that I don't have 220K rep. :P But also, for no particular reason except my own preference, I wanted to do a single command and no subshell. But thanks for the tips...I will incorporate in my planned edit.
    – B Layer
    Aug 4, 2017 at 15:51
  • 1
    The subshell was only for the local scope for IFS and the noglob option. I just found strange that you would change : to \n to later split on \n, instead of just splitting on : in the first place. See also compgen -c | grep MP3 in bash Aug 4, 2017 at 15:57
  • Well, I agree in retrospect that : for IFS makes a lot more sense.
    – B Layer
    Aug 4, 2017 at 15:58
  • @BLayer that was much simpler than my alternative (as in the answer from @Thegs), and +1 because I really never checked IFS possibility on find. Great! Thanks @Stéphane for making it even simpler, the local scope idea was perfect to alias it. But I have to add that letting the current dir on PATH worked for me when following your example. Also a non-"tabbable" solution, but it seems that's the maximum I'll get from Bash. Aug 9, 2017 at 6:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .