copying bash completion (without copying the actual code) - bash

Suppose I have a command git-local (it could be a Bash function or a binary in /usr/local/bin) and suppose I would like git-local to have the same tab completion as the command git has. Finally, suppose that I'm efficient (read lazy) and I don't want to go find the code that the git commmand uses to manually copy over and bloat my .bashrc (or whatever external file I paste it in and the source). Is there a simple way I can have git-local use the same autocompletion as git?

8.7 Programmable Completion Builtins:
If the -p option is supplied, or if no options are supplied, existing completion specifications are printed in a way that allows them to be reused as input.
Something like
$(complete -p git | awk '$NF="git-local"')
maybe?
E.g.:
$ complete -p foobar
-bash: complete: foobar: no completion specification
$ complete -p traceroute
complete -F _known_hosts traceroute
$ $(complete -p traceroute | awk '$NF="foobar"')
$ complete -p foobar
complete -F _known_hosts foobar

Related

bash, how to dot source a downloaded file (using curl) into bash

I have .sh file that I would like to dotsource into my running environment. This does not work:
curl -s https://raw.githubusercontent.com/bla/bla/master/stuff.sh | bash
The above does not work, i.e. The script runs, but the environment variables and things inside stuff.sh are not dotsourced into the running environment. I also tried:
. curl -s https://raw.githubusercontent.com/bla/bla/master/stuff.sh | bash
curl -s https://raw.githubusercontent.com/bla/bla/master/stuff.sh | bash source
curl -s https://raw.githubusercontent.com/bla/bla/master/stuff.sh | source bash
All fail. Would appreciate knowing how this can be done?
I am not a bash expert, but if you are willing to accept some drawbacks, the easiest method to do that is without pipes. I believe that it should be possible when you separate download and sourcing:
prompt># curl -s https://raw.githubusercontent.com/bla/bla/master/stuff.sh > ./stuff.sh
prompt># . ./stuff.sh
From the bash manual (man bash), in the chapter about the builtin source command:
Read and execute commands from filename [...]
There is no mentioning about standard input as a possible source for the commands which should be sourced.
However, as hanshenrik stated in his answer, you always can use process substitution to create a temporary (and invisible on the file system) file which you can feed to source. The syntax is <(list), where <(list) is expanded to a unique file name chosen by bash, and list is a sequence of commands whose output is put into that file (the file does not appear on the file system, though).
Process substitution is documented in the bash manual (man bash) in a paragraph under that exact caption.
try
source <(curl -s https://raw.githubusercontent.com/bla/bla/master/stuff.sh)
i tried doing
curl -s https://raw.githubusercontent.com/bla/bla/master/stuff.sh | source /dev/stdin
but that didn't work for some reason, no idea why (anyone knows?)

How to "hide" an executable from a bash script?

I want to test the output of a bash script when one of the executables it depends on is missing, so I want to run that script with the dependency "hidden" but no others. PATH= ./script isn't an option because the script needs to run other executables before it reaches the statement I want to test. Is there a way of "hiding" an executable from a script without altering the filesystem?
For a concrete example, I want to run this script but hide the git executable (which is its main dependency) from it so that I can test its output under these conditions.
You can use the builtin command, hash:
hash [-r] [-p filename] [-dt] [name]
Each time hash is invoked, it remembers the full pathnames of the commands specified as name arguments, so they need not be searched for on subsequent invocations. ... The -p option inhibits the path search, and filename is used as the location of name. ... The -d option causes the shell to forget the remembered location of each name.
By passing a non-existent file to the -p option, it will be as if the command can't be found (although it can still be accessed by the full path). Passing -d undoes the effect.
$ hash -p /dev/null/git git
$ git --version
bash: /dev/null/git: command not found
$ /usr/bin/git --version
git version 1.9.5
$ hash -d git
$ git --version
git version 1.9.5
Add a function named git
git() { false; }
That will "hide" the git command
To copy #npostavs's idea, you can still get to the "real" git with the command builtin:
command git --version
Since we know the program is running in bash, one solution is to - instead of "hiding" the program - emulate the behaviour of bash in this circumstance. We can find out what bash does when a command isn't found quite easily:
$ bash
$ not-a-command > stdout 2> stderr
$ echo $?
127
$ cat stdout
$ cat stderr
bash: not-a-command: command not found
We can then write this behaviour to a script with the executable name, such as git in the question's example:
$ echo 'echo >&2 "bash: git: command not found" && exit 127' > git
$ chmod +x git
$ PATH="$PWD:$PATH" git
$ echo $?
127
$ cat stdout
$ cat stderr
bash: git: command not found

Where is the 'man' complete function?

When i type something like this:
$ complete -p | grep -ie '\<man\>'
or:
$ find /etc/bash_completion.d/ -iname '*man*'
or:
$ grep -rie '\<man\>' /etc/bash_completion.d/
I don't find any trace of the complete function for the man command!
The goal is to add this completion for one of my own-made function:
complete -F <the_man_complete_function> <my_man_related_command>
Any idea?
I can't give you complete solution, but I found the following on my system
If I run a new shell and type:
> complete -p man
bash: complete: man: no completion specification
If I try to autocomplete a man command with:
> man [tab][tab]
Display all 16625 possibilities? (y or n)
CTRL-C
Now:
> complete -p man
complete -F _man man
Why now? Don't have an answer, but now I can get:
> complete -p | grep _man
complete -F _man man
complete -F _man apropos
complete -F _man whatis
I hope this helps a bit. It is quite a mystery why it works only after using it first. Someone else an idea?
Bash function documentation can also be found using, e.g.,
help complete
I think you are asking where the man page is for the command 'complete' can be found. If so, complete is part of bash (if you are using bash), so just type 'man bash'.
I found a solution : to load the not-yet-loaded completion func, I just have to launch this in .bashrc:
. /usr/share/bash-completion/completions/man
Usefull links that helped me:
https://unix.stackexchange.com/questions/89409/bash-completion-not-working-for-some-commands
bash-completion - completion function defined the first time a command is invoked
EDIT
To avoid loading the man completion script on every bash session start, a better solution is to write a local completion script, to put in the directory $XDG_DATA_HOME/bash-completion/completions (typically ~/.local/share/bash-completion/completions), with the name of the desired command (as explained in the bash-completion FAQ).
Here's the code I put in the script (named myman):
#!/bin/bash
# Force the load of the man completion function if it is not loaded yet:
type -t _man >/dev/null || . /usr/share/bash-completion/completions/man
complete -F _man myman
With this method, the man completion function will be loaded only when required (by man, or myman).

How to run a series of vim commands from command prompt

I have four text files A.txt, B.txt, C.txt and D.txt
I have to perform a series of vim editing in all these files.
Currently how I am doing is open each files and do the same vim commands one by one.
Is it possible to make a script file which I can run from the command prompt, means without open the actual file for vim editing.
for example, if I have to perform the below vim commands after opening the A.txt file in vim editor:
:g/^\s*$/d
:%s/^/[/
:%s/\(.*\)\(\s\+\)\(.*\)/\3\2\1
:%s/$/]/
:%s/design/test/
Is it possible to make a script file and put all these commands including gvim A.txt (first command in the file).
and edit run the script file from command prompt.
If it is possible, please let me know how to do it and how it can be done with single or multiple files at a time?
vim -c <command> Execute <command> after loading the first file
Does what you describe, but you'll have to do it one file at a time.
So, in a windows shell...
for %a in (A,B,C,D) do vim -c ":g/^\s*$/d" -c "<another command>" %a.txt
POSIX shells are similar, but I don't have a machine in front of me at the moment.
I imagine you could load all the files at once and do it, but it would require repeating the commands on the vim command line for each file, similar to
vim -c "<command>" -c "<command>" -c ":n" (repeat the previous -c commands for each file.) <filenames go here>
EDIT: June 08 2014:
Just an FYI, I discovered this a few minutes ago.
vim has the command bufdo to do things to each buffer (file) loaded in the editor. Look at the docs for the bufdo command. In vim, :help bufdo
The amount of -c commands directly passed to Vim on the command-line is limited to 10, and this is not very readable. Alternatively, you can put the commands into a separate script and pass that to Vim. Here's how:
Silent Batch Mode
For very simple text processing (i.e. using Vim like an enhanced 'sed' or 'awk', maybe just benefitting from the enhanced regular expressions in a :substitute command), use Ex-mode.
REM Windows
call vim -N -u NONE -n -es -S "commands.ex" "filespec"
Note: silent batch mode (:help -s-ex) messes up the Windows console, so you may have to do a cls to clean up after the Vim run.
# Unix
vim -T dumb --noplugin -n -es -S "commands.ex" "filespec"
Attention: Vim will hang waiting for input if the "commands.ex" file doesn't exist; better check beforehand for its existence! Alternatively, Vim can read the commands from stdin. You can also fill a new buffer with text read from stdin, and read commands from stderr if you use the - argument.
Full Automation
For more advanced processing involving multiple windows, and real automation of Vim (where you might interact with the user or leave Vim running to let the user take over), use:
vim -N -u NONE -n -c "set nomore" -S "commands.vim" "filespec"
Here's a summary of the used arguments:
-T dumb Avoids errors in case the terminal detection goes wrong.
-N -u NONE Do not load vimrc and plugins, alternatively:
--noplugin Do not load plugins.
-n No swapfile.
-es Ex mode + silent batch mode -s-ex
Attention: Must be given in that order!
-S ... Source script.
-c 'set nomore' Suppress the more-prompt when the screen is filled
with messages or output to avoid blocking.
With all the commands you want to run on each file saved in a script, say "script.vim", you can execute that script on one file like this (as others have mentioned):
vim -c "source script.vim" A.txt
Taking this one step further, you can save your file at the end of the script, either by putting a :w command inside the script itself, or passing it from the command-line:
vim -c "source script.vim | w" A.txt
Now, you can run any command in Vim on multiple files, by using the argdo command. So your command turns into:
vim -c "argdo source script.vim | w" A.txt B.txt C.txt D.txt
Finally, if you want to quit Vim after running your script on every file, just add another command to quit:
vim -c "argdo source script.vim | w" -c "qa" A.txt B.txt C.txt D.txt
Try the following syntax:
ex foo.txt <<-EOF
g/^\s*$/d
%s/^/[/
%s/\(.*\)\(\s\+\)\(.*\)/\3\2\1
%s/$/]/
%s/design/test/
wq " Update changes and quit.
EOF
The ex command is equivalent to vim -E. Add -V1 for verbose output.
Alternative one-liner syntax is for example:
ex +"g/^\s*$/d" +"%s/^/[/" +"%s/design/test/" -cwq foo.txt
To load commands from the file, use -s cmds.vim.
You can also use shebang for Vim to parse the file from the argument.
For more examples, see:
How to edit files non-interactively (e.g. in pipeline)?
BashFAQ/021
JimR and Ingo have provided excellent answers for your use case.
Just to add one more way to do it, however, you could use my vimrunner plugin to script the interaction in ruby: https://github.com/AndrewRadev/vimrunner.
Example:
vim = Vimrunner.start
vim.edit "file.txt"
vim.insert "Hello"
vim.write
vim.kill
This can be useful for more complicated interactions, since you get the full power of a programming language.
Use vim -s ... to script not only colon commands, but also normal-mode commands such as = for formatting:
Create a file with all keystrokes that you want vim to execute.
Run vim -s SCRIPT-FILE FILE-TO-EDIT
For example: Let's say you want to use vim's = command to re-indent all the lines of myfile.html. First, using vim itself, make a file named myscript that has this:
gg=G:wq
(gg moves to the top of the file; =G re-indents from the current location to the end of the file; :wq<Enter> saves the file and exits.)
Then, run this to have vim launch, edit myfile.html, and save it:
vim -s myscript myfile.html

xargs with command that open editor leaves shell in weird state

I tried to make an alias for committing several different git projects. I tried something like
cat projectPaths | \
xargs -I project git --git-dir=project/.git --work-tree=project commit -a
where projectPaths is a file containing the paths to all the projects I want to commit. This seems to work for the most part, firing up vi in sequence for each project so that I can write a commit msg for it. I do, however, get a msg:
"Vim: Warning: Input is not from a terminal"
and afterward my terminal is weird: it doesn't show the text I type and doesn't seem to output any newlines. When I enter "reset" things pretty much back to normal, but clearly I'm doing something wrong.
Is there some way to get the same behavior without messing up my shell?
Thanks!
Using the simpler example of
ls *.h | xargs vim
here are a few ways to fix the problem:
xargs -a <( ls *.h ) vim
or
vim $( ls *.h | xargs )
or
ls *.h | xargs -o vim
The first example uses the xargs -a (--arg-file) flag which tells xargs to take its input from a file rather than standard input. The file we give it in this case is a bash process substitution rather than a regular file.
Process substitution takes the output of the command contained in <( ) places it in a filedescriptor and then substitutes the filedescriptor, in this case the substituted command would be something like xargs -a /dev/fd/63 vim.
The second command uses command substitution, the commands are executed in a subshell, and their stdout data is substituted.
The third command uses the xargs --open-tty (-o) flag, which the man page describes thusly:
Reopen stdin as /dev/tty in the child process before executing the
command. This is useful if you want xargs to run an interactive
application.
If you do use it the old way and want to get your terminal to behave again you can use the reset command.
The problem is that since you're running xargs (and hence git and hence vim) in a pipeline, its stdin is taken from the output of cat projectPaths rather than the terminal; this is confusing vim. Fortunately, the solution is simple: add the -o flag to xargs, and it'll start git (and hence vim) with input from /dev/tty, instead of its own stdin.
The man page for GNU xargs shows a similar command for emacs:
xargs sh -c 'emacs "$#" < /dev/tty' emacs
(in this command, the second "emacs" is the "dummy string" that wisbucky refers to in a comment to this answer)
and says this:
Launches the minimum number of copies of Emacs needed, one after the
other, to edit the files listed on xargs' standard input. This example
achieves the same effect as BSD's -o option, but in a more flexible and
portable way.
Another thing to try is using -a instead of cat:
xargs -a projectPaths -I project git --git-dir=project/.git --work-tree=project commit -a
or some combination of the two.
If you have GNU Parallel http://www.gnu.org/software/parallel/ installed you should be able to do this:
cat projectPaths |
parallel -uj1 git --git-dir={}/.git --work-tree={} commit -a
In general this works too:
cat filelist | parallel -Xuj1 $EDITOR
in case you want to edit more than one file at a time (and you have set $EDITOR to your favorite editor).
-o for xargs (as mentioned elsewhere) only works for some versions of xargs (notably it does not work for GNU xargs).
Watch the intro video to learn more about GNU Parallel http://www.youtube.com/watch?v=OpaiGYxkSuQ
Interesting! I see the exact same behaviour on Mac as well, doing something as simple as:
ls *.h | xargs vim
Apparently, it is a problem with vim:
http://talideon.com/weblog/2007/03/xargs-vim.cfm

Resources