Executing MSYS2 Emacs from Windows command line or shortcut - bash

I recently installed MSYS2 with Emacs (64-bit) and am currently calling that Emacs from a Windows shortcut. It works fine, exactly like if I had downloaded the Emacs executable for Windows and unzipped it somewhere. Which is to say, it picks up all of my Windows environment variables and such.
For various reasons, I would prefer to run Emacs from an MSYS2 bash shell and use the environment variables in that shell. As it stands, I can open an MSYS2 MINGW64 shell, type emacs on the command line, and everything works the way I want it to.
Now I would like to package the whole thing up into either a one-liner I can stuff into to a Windows shortcut or a script I can call from a Windows shortcut. With the help of this post, I came up with the following:
C:\msys64\bin\mintty.exe /bin/env MSYSTEM=MINGW64 /bin/bash -l -c /mingw64/bin/emacs
This successfully opens Emacs, but fails to load the .bashrc file that I source in .bash_profile in the usual manner:
if [ -f "${HOME}/.bashrc" ] ; then
source "${HOME}/.bashrc"
fi
I define a function in .bashrc that I call in .bash_profile, so this is kind of important. It did not take much effort to realize that the problem is that HOME is not defined, so .bashrc is simply not found. However, if I define HOME like so:
C:\msys64\bin\mintty.exe /bin/env HOME=/home/alanhr MSYSTEM=MINGW64 /bin/bash -l -c /mingw64/bin/emacs
I get exactly the same result: .bashrc is not found and my function is not executed. Here's where it gets weird. If I simply leave off the call to emacs like so:
C:\msys64\bin\mintty.exe /bin/env HOME=/home/alanhr MSYSTEM=MINGW64 /bin/bash -l
I get a bash shell where .bashrc has been loaded correctly and my function is correctly executed. I can type emacs on the command line and have it function exactly as I want it to.
This feels like a classic case of missing something that is right under my nose, but I have read the bash man page to no avail. Does anyone have any idea how I can make this work?

It is the -i option to load .bashrc. The following works for me:
C:\msys64\usr\bin\mintty.exe -w hide /bin/env MSYSTEM=MINGW64 /bin/bash -l -i -c /mingw64/bin/emacs

Related

Executing a script in MSYS2/MinGW

On Windows, if I start c:\msys64\mingw64.exe, it opens a shell, where I can build my project, let's say by calling a release bash script (to simplify). Everything works fine.
Now, I would like to execute my release script on mingw64 directly, without interaction.
I tried:
c:\msys64\mingw64.exe /c/the/full/path/release
A window opens and closes, it does not work.
I attempted to use bash directly, but it seems the environment is not correctly set:
> c:\msys64\usr\bin\bash -c ls
/usr/bin/bash: ls: command not found
> c:\msys64\usr\bin\bash -c /bin/ls
... it works ...
So it is obvious that the environment is not the same as when execute c:\msys64\mingw64.exe then call ls.
How to execute my release script as if I were in the shell started by mingw64.exe?
To run a Bash shell script in MSYS2 without showing a window, you should right-click on your Desktop or somewhere else in Windows Explorer, select "New", select "Shortcut", and then enter something like this for the shortcut target:
C:\msys64\usr\bin\mintty.exe -w hide /bin/env MSYSTEM=MINGW64 /bin/bash -l /c/Users/rom1v/project/release.sh
Note that there are 4 paths in here. The path to mintty and release.sh are absolute paths that you will need to adjust. The paths to env and bash are relative to your MSYS2 installation directory. Note also that the first path must be a standard Windows path, since Windows expects that when it is running a shortcut.
Explanation
It might seem odd to use MinTTY for a non-interactive script, but we need to use some program that was compiled for the Windows subsystem (-mwindows option to GCC), or else Windows will automatically start a new console when we run the program. We pass the -w hide option to MinTTY to tell it not to actually show a window. Everything after that option is interpreted by MinTTY as a command to run.
So MinTTY will run /bin/env from the MSYS2 distribution and pass the remainder of the arguments on to it. This is a handy utility that is actually a standard part of Linux as well as MSYS2. It sets the MSYSTEM environment variable to MINGW64 (which is important later) and then it runs /bin/bash with the remainder of the command-line arguments.
We pass -l to Bash so that it acts as a login script, and runs certain startup scripts. In particular, the /etc/profile script from MSYS2 is essential because it looks at the MSYSTEM environment variable, sees that it is MINGW64, and then sets a bunch of other environment variables (e.g. PATH) to give you the MinGW 64-bit shell environment.
Finally, we pass the name of your script as the main argument to bash, so it will run that script after running the initialization scripts.
Error handling
Note that if your Bash script has an error, you won't get any notification, because the shortcut above doesn't open any console windows. I personally would find that pretty annoying. I'd probably remove the -w hide option, then make a wrapper bash script that just does something like:
run_my_main_script || sleep 10000
So if the main script is successful, exit right away, otherwise keep the window open for 10000 seconds. You don't have to even put that wrapper script in its own file, you can just put it in the shortcut as the argument to Bash's -c option (don't forget to wrap it in double quotes).
Thanks to the answers from #David Grayson, I managed to call my release script with msys2/mingw from a Windows console (cmd), with additional directories (for Java and Meson) in $PATH:
c:\msys64\usr\bin\env MSYSTEM=MINGW64 c:\msys64\usr\bin\bash -l -c "PATH=\"/c/Program Files/Java/jdk1.8.X_XXX/bin:/c/Program Files/Meson:$PATH\" /c/Users/rom1v/project/release"
add an supplement to the above: if u want to the output of shell script
reference:https://mintty.github.io/mintty.1.html
-l, --log FILE|-
Copy all output into the specified log file, or standard output if a dash is given instead of a file name. (Implies -o Logging=yes.)
If FILE contains %d it will be substituted with the process ID. See description of equivalent option "Log file" (Log=) below for further formatting options and hints.
Note that logging can be toggled from the extended context menu.
Add A complete example:
C:\msys64\usr\bin\mintty.exe -w hide -l - c:\msys64\usr\bin\env MSYSTEM=MINGW64 c:\msys64\usr\bin\bash -l -c "PATH=\"$PATH\" /C/Users/Administrator/Desktop/myProject/Demo_C_C++/shell/textProcess/bookNoteHandler.sh" | find /v "/v:Displays all lines that don't contain the specified"
=========

Call bash function using vim external command

I use vim's :! external command function all the time, usually providing % as an argument to the shell command. For example :
:!psql -f %
I also have a lot of bash shell functions in my .bashrc that I use. For example:
psql-h1 ()
{
/usr/bin/psql -hh1 -d mydb "$#"
}
These bash functions aren't available from :! inside of vim. Is there a way to make them available?
Export your functions. That is:
psql-h1() { /usr/bin/psql -hh1 -d mydb "$#"; }
export -f psql-h1 ### <-- THIS RIGHT HERE
This will make them available to any copy of bash run as a child process, even if it's a noninteractive shell and so doesn't read .bashrc.
An alternative to exporting your functions (which may no reach Vim is there's a non-Bash shell in between; see here for such a case), you can instruct Vim to start an interactive shell, so that your .bashrc is read. Just pass the -i flag to Bash, via Vim's :help 'shellcmdflag'.
:set shcf=-ic
This answer assumes your vim isn't actually using bash to invoke the remote commands - this can be tested by running :!echo $0 in vim.
Specifically for vim, add:
set shell=/bin/bash
to your .vimrc.
In general, there's two strategies I've found to sometimes work when trying to get other programs to invoke my preferred shell:
export SHELL=/bin/bash
in eg. the .profile file, or:
ln -fsn /bin/bash /bin/sh
which updates the sh symlink to point to bash instead.
On many systems (certainly Ubuntu), /bin/sh is a symlink to a sh-compatible shell (such as bash), rather than the sh shell itself. Given this fact, some programs (I've seen this behaviour in GHC) seem to invoke /bin/sh, so changing where it points to will cause the programs to use bash instead.
The accepted answer didn't work for me. I'm going to go with setting shcf, as suggested elsewhere:
:set shcf=-ic
but another solutions is
!source ~/.bashrc && psql ...
Unfortunately, no solution allows the auto-completion for the command I'm creating to work properly. (The auto_completions suggested are for names of files in my current directory, rather than the ones I specified as follows in .bashrc
complete -F _generate_foo_completions foo

How to restart vim from a bash script?

I want to restart vim from a bash script so that vim picks up out-of-band changes. I almost have it working but I am stuck trying to determine what to use to launch vim.
Here's what I have:
#!/usr/bin/env bash
local servername=$(vim --serverlist)
[ -n "$servername" ] && {
vim --servername "$servername" --remote-send '<C-\><C-N>:mks! _session.vim<CR>:wqa<CR>'
vim -S _session.vim
sleep 1
rm _session.vim
}
The problem is the vim called by the script is the very obsolete system vim at /usr/bin/vim, not "my" vim which is an alias to mvim -v (where mvim is the launch script which comes with MacVim).
This has two unfortunate consequences: (1) the system vim doesn't recognise --serverlist; (2) even if it did my script would subsequently launch the wrong vim.
What's the best way to invoke the vim on my path?
The default vim is never built with +clientserver so the portability you are afraid to loose was never there to begin with.
Aliases are not expanded in bash scripts so your script won't see mvim -v if you don't tell it explicitly to use that. Furthermore, your vim is an alias so it is not in your PATH.
You could define an environment variable somewhere near the top of your script and use it instead of vim:
#!/usr/bin/env bash
VIM='/path/to/mvim'
"$VIM" -v whatever
Or turn your alias into a proper script.
Or, maybe, place mvim earlier in your PATH and call mvim -v explicitly.

Activating a VirtualEnv using a shell script doesn't seem to work

I tried activating a VirtualEnv through a shell script like the one below but it doesn't seem to work,
#!/bin/sh
source ~/.virtualenvs/pinax-env/bin/activate
I get the following error
$ sh virtualenv_activate.sh
virtualenv_activate.sh: 2: source: not found
but if I enter the same command on terminal it seems to work
$ source ~/.virtualenvs/pinax-env/bin/activate
(pinax-env)gautam#Aspirebuntu:$
So I changed the shell script to
#!/bin/bash
source ~/.virtualenvs/pinax-env/bin/activate
as suggested and used
$ bash virtualenv_activate.sh
gautam#Aspirebuntu:$
to run the script .
That doesn't throw an error but neither does that activate the virtual env
So any suggestion on how to solve this problem ?
PS : I am using Ubuntu 11.04
TLDR
Must run the .sh script with source instead of the script solely
source your-script.sh
and not
your-script.sh
Details
sh is not the same as bash (although some systems simply link sh to bash, so running sh actually runs bash). You can think of sh as a watered down version of bash. One thing that bash has that sh does not is the "source" command. This is why you're getting that error... source runs fine in your bash shell. But when you start your script using sh, you run the script in an shell in a subprocess. Since that script is running in sh, "source" is not found.
The solution is to run the script in bash instead. Change the first line to...
#!/bin/bash
Then run with...
./virtualenv_activate.sh
...or...
/bin/bash virtualenv_activate.sh
Edit:
If you want the activation of the virtualenv to change the shell that you call the script from, you need to use the "source" or "dot operator". This ensures that the script is run in the current shell (and therefore changes the current environment)...
source virtualenv_activate.sh
...or...
. virtualenv_activate.sh
As a side note, this is why virtualenv always says you need to use "source" to run it's activate script.
source is an builtin shell command in bash, and is not available in sh. If i remember correctly then virtual env does a lot of path and environment variables manipulation. Even running it as bash virtualenv_blah.sh wont work since this will simply create the environment inside the sub-shell.
Try . virtualenv_activate.sh or source virtualenv_activate.sh this basically gets the script to run in your current environment and all the environment variables modified by virtualenv's activate will be available.
HTH.
Edit: Here is a link that might help - http://ss64.com/bash/period.html
On Mac OS X your proposals seems not working.
I have done it this way. I'am not very happy with solution, but share it anyway here and hope, that maybe somebody will suggest the better one:
In activate.sh I have
echo 'source /Users/andi/.virtualenvs/data_science/bin/activate'
I give execution permissions by: chmod +x activate.sh
And I execute this way:
`./activate.sh`
Notice that there are paranthesis in form of ASCII code 96 = ` ( Grave accent )
For me best way work as below.
Create start-my-py-software.sh and pest below code
#!/bin/bash
source "/home/snippetbucket.com/source/AIML-Server-CloudPlatform/bin/activate"
python --version
python /home/snippetbucket.com/source/AIML-Server-CloudPlatform/main.py
Give file permission to run like below.
chmod +x start-my-py-software.sh
Now run like below
.start-my-py-software.sh
and that's it, start my python based server or any other code.
ubuntu #18.0
In my case, Ubuntu 16.04, the methods above didn't worked well or it needs much works.
I just made a link of 'activate' script file and copy it to home folder(or $PATH accessible folder) and renamed it simple one like 'actai'.
Then in a terminal, just call 'source actai'. It worked!

How can I open Emacs on Mac OS X such that it is loaded with my $PATH?

I'm running Snow Leopard, and trying to run Emacs such that when I start it up, the output of (getenv "PATH") is the same as the output in Terminal.app of echo $PATH.
In other words, I want to start up Emacs from /Applications/Emacs.app and have it start with my $PATH. I haven't been able to figure out how to do this within emacs, or with how I start emacs up. So I've spent most of my effort trying to come up with a shell script that I can wrap with something like Platypus or Appify.
So the closest thing I have right now is:
echo MYPASSWD | sudo -S -u USERNAME -i nohup /Applications/Emacs.app/Contents/MacOS/Emacs > /dev/null &
which fails because it seems that nohup throws away my $PATH, despite the -i flag. The following does not throw away my PATH but open a superfluous Terminal.app:
echo MYPASSWD | sudo -S -u USERNAME -i open /Applications/Emacs.app/Contents/MacOS/Emacs > /dev/null &
I've tried running this through do shell script in an AppleScript, also to no avail.
Am I missing something basic? It doesn't seem like this should be hard. Thanks!
I wrote a little elisp a while ago to parse the output of env and apply it to the Emacs environment because I didn't want to maintain a plist. The code's at http://paste.lisp.org/display/111574.
GUI applications are not launched by a traditional shell and do not inherit environment variables from the usual places like .profile, .bash_profile, etc.
http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/EnvironmentVars.html%23//apple_ref/doc/uid/20002093-BCIJIJBH explains that ~/.MacOSX/environment.plist is the place you probably want to add your favorite equivalent of $PATH.
I ran into the same problem and here's how I worked around it. I created ~/.bashrc and populated it with:
source /etc/profile
source ~/.profile
You have to export the PATH to the location of the .app so it would be something like this
export PATH=$PATH:/path/to/the/program
That should allow you to just type EMACS and it will start up.

Resources