Is it possible to have separate bash completion functions for separate commands which happen to share the same name? - bash

I have two separate scripts with the same filename, in different paths, for different projects:
/home/me/projects/alpha/bin/hithere and /home/me/projects/beta/bin/hithere.
Correspondingly, I have two separate bash completion scripts, because the proper completions differ for each of the scripts. In the completion scripts, the "complete" command is run for each completion specifying the full name of the script in question, i.e.
complete -F _alpha_hithere_completion /home/me/projects/alpha/bin/hithere
However, only the most-recently-run script seems to have an effect, regardless of which actual version of hithere is invoked: it seems that bash completion only cares about the filename of the command and disregards path information.
Is there any way to change this behavior so that I can have these two independent scripts with the same name, each with different completion functions?
Please note that I'm not interested in a solution which requires alpha to know about beta, or which would require a third component to know about either of them--that would defeat the purpose in my case.

The Bash manual describes the lookup process for completions:
If the command word is a full pathname, a compspec for the full pathname is searched for first. If no compspec is found for the full pathname, an attempt is made to find a compspec for the portion following the final slash. If those searches do not result in a compspec, any compspec defined with the -D option to complete is used as the default.
So the full path is used by complete, but only if you invoke the command via its full path. As for getting completions to work using just the short name, I think your only option (judging from the spec) is going to be some sort of dynamic hook that determines which completion function to invoke based on the $PWD - I don't see any evidence that Bash supports overloading a completion name like you're envisioning.

Yes, this is possible. But it's a bit tricky. I am using this for a future scripting concept I am developing: All scripts have the same name as they are build scripts, but still bash completion can do its job.
For this I use a two step process: First of all I place a main script in ~/.config/bash_completion.d. This script is designed to cover all scripts of the particular shared script name. I configured ~/.bashrc in order to load that bash completion file for these scripts.
The script will obtain the full file path of the particular script file I want to have bash completion for. From this path I generate an identifier. For this identifier there exists a file that provides actual bash completion data. So if bash completion is performed the bash completion function from the main bash completion script will check for that file and load it's content. Then it will continue with regular bash completion operation.
If you have two scripts with the same name you will have two different identifiers as those scripts share the same name but have different paths. Therefore two different configurations for bash completion can be used.
This concept works like a charm.
NOTE: I will update this answer soon providing some source code.

Related

Is it possible to modify compopts dynamically from an external completion command?

I am writing a bash completion program in Golang. In fact, the program is its own completion program as it looks for the COMP_LINE environment variable and if it is present, it outputs the completion options, and if not, just proceeds to run the main program.
The completion is then installed with the following:
complete -C /path/to/my-program my-program
This works well. For most of my completions, I want a space to be added after the word has been completed, however for a few flags I do not want this to occur.
When completion is defined, you can set a -o nospace option to omit the trailing space when completing a word. However then all completions that need a space have to have one added explicitly to the completion word list.
Is there any way that my program can modify the complete opts dynamically based on what completion it is returning? Is this exposed as an environment variable that a completion command could set?
I would like to avoid having to append a space to all other completions just to avoid one in the edge case for the one flag I don't want that to happen on.
My Perl framework (Perinci::CmdLine) also does the same: the scripts are their own completion, activated using complete -C SCRIPTNAME SCRIPTNAME (when the script is in PATH). Completing using an external command has its pro's and con's compared to using shell function. To solve the problem you encountered, I output a dummy answer with an extra space. Since there are more than one answer, bash no longer automatically adds a space. So instead of just returning (in JSON notation):
["-o"]
you return:
["-o","-o "]
I also use this trick when doing path completion. To allow user completing a path by "drilling down", when there is a single directory match I output:
["dirname/","dirname/ "]
so the user can Tab again to drill down inside path instead of getting a space after "dirname/ " and having to backspace and Tab again.

bash completion directory style partial suggestion printing

I am working on a script called to that bookmarks file system locations in bash (with limited support for zsh). You can find the source here: https://github.com/resultsreturned/to
The script supports tab completion for all operations.
Say you have "bookmarked" the location of a folder:
mara#andromeda:~/bin/android-sdk-linux$ to -b android
The script then allows you to access subfolders of the bookmark, like so:
mara#andromeda:~$ to android/docs/
mara#andromeda:~/bin/android-sdk-linux/docs$
Pressing tab twice will give suggestions:
mara#andromeda:~$ to android/s<TAB><TAB>
android/samples/ android/sd-card/ android/sources/ android/system-images/
However, I would rather that the suggestions print only the portion of the path that is not currently input into the buffer. For example, the cd command:
mara#andromeda:~/bin$ cd android-sdk-linux/s<TAB><TAB>
samples/ sd-card/ sources/ system-images/
Note how the suggestions only contain the directory level that is currently being input.
So the question is, how do you control the way bash prints suggestions? Is it possible to do this independently of the completion wordlist (for compgen/complete)? Is there some way to define a regex that would process the completion wordlist for printing as suggestions?
Since feature requests to mark a comment as an answer remain declined, I copy the above solution here.
So the solution turns out to be using -o filenames when invoking complete. Showing only the substrings of COMPREPLY bash completion options to the user – resultsreturned

Get last bash command including pipes

I wrote a script that's retrieving the currently run command using $BASH_COMMAND. The script is basically doing some logic to figure out current command and file being opened for each tmux session. Everything works great, except when user runs a piped command (i.e. cat file | less), in which case $BASH_COMMAND only seems to store the first command before the pipe. As a result, instead of showing the command as less[file] (which is the actual program that has the file open), the script outputs it as cat[file].
One alternative I tried using is relying on history 1 instead of $BASH_COMMAND. There are a couple issues with this alternative as well. First, it does not auto-expand aliases, like $BASH_COMMAND does, which in some cases could cause the script to get confused (for example, if I tell it to ignore ls, but use ll instead (mapped to ls -l), the script will not ignore the command, processing it anyway), and including extra conditionals for each alias doesn't seem like a clean solution. The second problem is that I'm using HISTIGNORE to filter out some common commands, which I still want the script to be aware of, using history will just make the script ignore the last command unless it's tracked by history.
I also tried using ${#PIPESTATUS[#]} to see if the array length is 1 (no pipes) or higher (pipes used, in which case I would retrieve the history instead), but it seems to always only be aware of 1 command as well.
Is anyone aware of other alternatives that could work for me (such as another variable that would store $BASH_COMMAND for the other subcalls that are to be executed after the current subcall is complete, or some way to be aware if the pipe was used in the last command)?
i think that you will need to change a bit your implementation and use "history" command to get it to work. Also, use the command "alias" to check all of the configured alias.. the command "which" to check if the command is actually stored in any PATH dir. good luck

Result of shell script as build setting

Is it possible to run a shell script and use its result as a user defined macro in Xcode?
Basically I just want the result of a shell script to be put in a variable so it gets set in Info.plist (just like ${EXECUTABLE_NAME} etc.)
For example:
If I add $(/usr/bin/whoami) as a build setting condition (at the bottom of settings of the build configuration) it just sets an empty string.
See this question for a couple of different approaches. All of them require add a "Run Script" build phase.
Assuming a bash like shell, and given an almost complete lack of context for your problem, try
EXECUTABLE_NAME=$( scriptToGetEXEC_NAME )
PRODUCT_NAME=$( scriptToGetPROD_NAME)
The $( ... cmd ... ) construct is called command substitution. What this means is the when the shell processor scans each line of code, if first looks to see if there are any $(...) embedded (and other things to). If there are, it spawns a new shell, executes the code inside, and if any text is returned, it is embedded in the command line and THEN the shell scans the line again, and eventually executes everything from left to right, assuming that the first word will turn into a built-in command or a command in the PATH.
I hope this helps.
P.S. as you appear to be a new user, if you get an answer that helps you please remember to mark it as accepted, and/or give it a + (or -) as a useful answer.

Is it possible to override hashbang/shebang path behavior

I have a bunch of scripts (which can't be modified) written on Windows. Windows allows relative paths in its #! commands. We are trying to run these scripts on Unix but Bash only seems to respect absolute paths in its #! directives. I've looked around but haven't been able to locate an option in Bash or a program designed to replace and interpreter name. Is it possible to override that functionality -- perhaps even by using a different shell?
Typically you can just specify the binary to execute the script, which will cause the #! to be ignored. So, if you have a Python script that looks like:
#!..\bin\python2.6
# code would be here.
On Unix/Linux you can just say:
prompt$ python2.6 <scriptfile>
And it'll execute using the command line binary. I view the hashbang line as one which asks the operating system to use the binary specified on the line, but you can override it by not executing the script as a normal executable.
Worst case you could write some wrapper scripts that would explicitly tell the interpreter to execute the code in the script file for all the platforms that you'd be using.

Resources