bash, how to dot source a downloaded file (using curl) into bash - 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?)

Related

command substitution not working in alias?

I wanted to make an alias for launching a vim session with all the c/header/makefiles, etc loaded into the buffer.
shopt -s extglob
alias vimc="files=$(ls -A *.?(c|h|mk|[1-9]) .gitconfig [mM]akefile 2>/dev/null); [[ -z $files ]] || vim $files"
When I run the command enclosed within the quotations from the shell, it works but when run as the alias itself, it does not. Running vimc, causes vim to launch only in the first matched file(which happens to be the Makefile) and the other files(names) are executed as commands for some reason(of course unsuccessfully). I tried fiddling around and it seems that the command substitution introduces the problem. Because running only the ls produces expected output.
I cannot use xargs with vim because it breaks the terminal display.
Can anyone explain what might be causing this ?
Here is some output:
$ ls
Makefile readme main.1 main.c header.h config.mk
$ vimc
main.1: command not found
main.c: command not found
.gitignore: command not found
header.h: command not found
config.mk: command not found
On an related note, would it be possible to do what I intend to do above in a "single line", i.e without storing it into a variable files and checking to see if it is empty, using only the output stream from ls?

bash script yields a different result when sourced

Could you help me, why this script works when sourced (or even directly on console) and does not work on a script?
I have checked and in any case I'm using the same bash in /bin/ and always 4.4.19(1)-release (checked with $BASH_VERSION).
Moreover I tried removing shebang but nothing changes.
#!/bin/bash
fname=c8_m81l_55.fit
bname=${fname%%+(_)+([0-9]).fit}
echo $bname
GIving these results:
test:~$ ./test.sh
c8_m81l_55.fit
test:~$ . ./test.sh
c8_m81l
Bash does not recognize +(pattern) syntax unless extglobs are enabled, and they are disabled by default. Apparently your bash setup enables them in interactive sessions; that's why your script works only when sourced in an interactive shell.
To fix that, either enable extglobs within the script by this command:
shopt -s extglob
Or use an alternative that works irrespective of shell's interactiveness:
bname=$(sed 's/__*[0-9][0-9]*\.fit$//' <<< $fname)
# with GNU sed it'd look like:
bname=$(sed -E 's/_+[0-9]+\.fit$//' <<< $fname)

copying bash completion (without copying the actual code)

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

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