Reputation: 2979
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
?
Upvotes: 24
Views: 25340
Reputation: 52817
I'd like to extend @Tiago Becerra Paolini's answer here, which I've upvoted, and my own answer here: Installing & setting up MSYS2 from scratch, including adding all 7 profiles to Windows Terminal....
Notes:
ucrt64
terminal.C:\msys64
, making msys2_shell.cmd
available at C:\msys64\msys2_shell.cmd
.Important: for any case where you have a command plus arguments, not just a command, be sure to wrap the whole set of "command plus arguments" in single quotes, as shown in the examples below.
# Run in the MSYS2 msys2 terminal
/c/msys64/msys2_shell.cmd -defterm -here -no-start -msys2 -shell bash -c 'some_command_and_args'
# Run in the MSYS2 mingw32 **legacy** terminal
/c/msys64/msys2_shell.cmd -defterm -here -no-start -mingw32 -shell bash -c 'some_command_and_args'
# Run in the MSYS2 mingw64 terminal
/c/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -shell bash -c 'some_command_and_args'
# [**RECOMMENDED**] Run in the MSYS2 UCRT64 terminal
/c/msys64/msys2_shell.cmd -defterm -here -no-start -ucrt64 -shell bash -c 'some_command_and_args'
# Run in the MSYS2 clang64 terminal
/c/msys64/msys2_shell.cmd -defterm -here -no-start -clang64 -shell bash -c 'some_command_and_args'
# Run in the MSYS2 clang32 **legacy** terminal
/c/msys64/msys2_shell.cmd -defterm -here -no-start -clang32 -shell bash -c 'some_command_and_args'
# Run in the MSYS2 clangarm64 terminal
/c/msys64/msys2_shell.cmd -defterm -here -no-start -clangarm64 -shell bash -c 'some_command_and_args'
To see which terminal a command is running in, run echo $MSYSTEM
in the terminal.
Example runs:
/c/msys64/msys2_shell.cmd -defterm -here -no-start -msys2 -shell bash -c 'echo "$MSYSTEM"'
/c/msys64/msys2_shell.cmd -defterm -here -no-start -mingw32 -shell bash -c 'echo "$MSYSTEM"'
/c/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -shell bash -c 'echo "$MSYSTEM"'
/c/msys64/msys2_shell.cmd -defterm -here -no-start -ucrt64 -shell bash -c 'echo "$MSYSTEM"'
/c/msys64/msys2_shell.cmd -defterm -here -no-start -clang64 -shell bash -c 'echo "$MSYSTEM"'
/c/msys64/msys2_shell.cmd -defterm -here -no-start -clang32 -shell bash -c 'echo "$MSYSTEM"'
/c/msys64/msys2_shell.cmd -defterm -here -no-start -clangarm64 -shell bash -c 'echo "$MSYSTEM"'
Example output:
MSYS
MINGW32
MINGW64
UCRT64
CLANG64
CLANG32
CLANGARM64
From a Windows Command Prompt, you just need to use Windows-style paths is all. This also works just fine in the MSYS-based Bash terminals in Windows, since MSYS2 terminals do automatic path translation, so you can also run these commands from within an MSYS2 terminal.
Example:
C:\msys64\msys2_shell.cmd -defterm -here -no-start -msys2 -shell bash -c 'echo "$MSYSTEM"'
C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw32 -shell bash -c 'echo "$MSYSTEM"'
C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw64 -shell bash -c 'echo "$MSYSTEM"'
C:\msys64\msys2_shell.cmd -defterm -here -no-start -ucrt64 -shell bash -c 'echo "$MSYSTEM"'
C:\msys64\msys2_shell.cmd -defterm -here -no-start -clang64 -shell bash -c 'echo "$MSYSTEM"'
C:\msys64\msys2_shell.cmd -defterm -here -no-start -clang32 -shell bash -c 'echo "$MSYSTEM"'
C:\msys64\msys2_shell.cmd -defterm -here -no-start -clangarm64 -shell bash -c 'echo "$MSYSTEM"'
Example output: exact same as above.
From a Windows PowerShell, you need to use the &
operator to run the command.
Example:
& "C:\msys64\msys2_shell.cmd" -defterm -here -no-start -msys2 -shell bash -c 'echo "$MSYSTEM"'
& "C:\msys64\msys2_shell.cmd" -defterm -here -no-start -mingw32 -shell bash -c 'echo "$MSYSTEM"'
& "C:\msys64\msys2_shell.cmd" -defterm -here -no-start -mingw64 -shell bash -c 'echo "$MSYSTEM"'
& "C:\msys64\msys2_shell.cmd" -defterm -here -no-start -ucrt64 -shell bash -c 'echo "$MSYSTEM"'
& "C:\msys64\msys2_shell.cmd" -defterm -here -no-start -clang64 -shell bash -c 'echo "$MSYSTEM"'
& "C:\msys64\msys2_shell.cmd" -defterm -here -no-start -clang32 -shell bash -c 'echo "$MSYSTEM"'
& "C:\msys64\msys2_shell.cmd" -defterm -here -no-start -clangarm64 -shell bash -c 'echo "$MSYSTEM"'
Example output: exact same as above.
You can also create a Bash function to wrap the command in order to significantly simplify the call. Here is a Bash wrapper function:
# Run the passed-in command + arguments inside the MSYS2 UCRT64 terminal
# on Windows
msys2_ucrt64() {
# IMPORTANT!: use `"$*"` instead of `"$@"` here or commands with arguments
# won't work.
/c/msys64/msys2_shell.cmd -defterm -here -no-start -ucrt64 -shell bash -c "$*"
}
# Another example wrapper function
msys2_clang64() {
/c/msys64/msys2_shell.cmd -defterm -here -no-start -clang64 -shell bash -c "$*"
}
Here are some example runs and their outputs. Notice that single quotes around the set of "command plus arguments" are not needed anymore, which helps a lot for really complicated commands and arguments. But, in this case, we want to force the environment variable MSYSTEM
to be expanded in the inner, or sub-shell, not the current, or outer shell, so we must surround just $MSYSTEM
with single quotes to prevent immediate variable expansion.
Example calls:
msys2_ucrt64 echo '$MSYSTEM'
msys2_clang64 echo '$MSYSTEM'
Example output:
UCRT64
CLANG64
Let's improve the above wrapper by making the ucrt64
part a parameter to the function so that a single wrapper can handle all 7 MSYS2 terminals.
Let's also surround the call with some marker prints to make it obvious when looking at output in the terminal that it was called from within this specified sub-shell.
# Run the passed-in command + arguments inside the specified MSYS2 terminal
# on Windows.
# - See: https://stackoverflow.com/a/79201770/4561887
#
# Usage: msys2_terminal <terminal> <command> [<args>...]
#
# Example: msys2_terminal ucrt64 echo '$MSYSTEM'
#
msys2_terminal() {
local terminal="$1"
# Remove the first argument from the list of arguments
shift
# IMPORTANT!: use `"$*"` instead of `"$@"` here or commands with arguments
# won't work.
local command="$*"
# Ensure the terminal is one of the 7 MSYS2 terminals.
# - We will also accept `msys` as an alias for `msys2`, since
# `msys2_shell.cmd` does too.
case "$terminal" in
msys|msys2|mingw32|mingw64|ucrt64|clang64|clang32|clangarm64)
;;
*)
echo "Invalid terminal: $terminal"
return 1
;;
esac
# Print the terminal name to make it obvious in the terminal output
echo "--Running in MSYS2 $terminal terminal...START--"
/c/msys64/msys2_shell.cmd -defterm -here -no-start -"$terminal" -shell bash -c "$command"
error_code="$?"
echo "--Running in MSYS2 $terminal terminal...END--"
return "$error_code"
}
# Convenience alias
alias ucrt64="msys2_terminal ucrt64"
Example calls:
msys2_terminal msys echo '$MSYSTEM'
msys2_terminal msys2 echo '$MSYSTEM'
msys2_terminal mingw32 echo '$MSYSTEM'
msys2_terminal mingw64 echo '$MSYSTEM'
msys2_terminal ucrt64 echo '$MSYSTEM' # the preferred terminal
msys2_terminal clang64 echo '$MSYSTEM'
msys2_terminal clang32 echo '$MSYSTEM'
msys2_terminal clangarm64 echo '$MSYSTEM'
Example output:
--Running in MSYS2 msys terminal...START--
MSYS
--Running in MSYS2 msys terminal...END--
--Running in MSYS2 msys2 terminal...START--
MSYS
--Running in MSYS2 msys2 terminal...END--
--Running in MSYS2 mingw32 terminal...START--
MINGW32
--Running in MSYS2 mingw32 terminal...END--
--Running in MSYS2 mingw64 terminal...START--
MINGW64
--Running in MSYS2 mingw64 terminal...END--
--Running in MSYS2 ucrt64 terminal...START--
UCRT64
--Running in MSYS2 ucrt64 terminal...END--
--Running in MSYS2 clang64 terminal...START--
CLANG64
--Running in MSYS2 clang64 terminal...END--
--Running in MSYS2 clang32 terminal...START--
CLANG32
--Running in MSYS2 clang32 terminal...END--
--Running in MSYS2 clangarm64 terminal...START--
CLANGARM64
--Running in MSYS2 clangarm64 terminal...END--
Example call using the ucrt64
alias above:
ucrt64 echo '$MSYSTEM'
Output:
--Running in MSYS2 ucrt64 terminal...START--
UCRT64
--Running in MSYS2 ucrt64 terminal...END--
If you call the msys2_terminal
function with an invalid terminal name, you will get an error message and a non-zero return code.
Example run and output. Note that "$?"
is the return code of the last command run:
$ msys2_terminal msyss2 echo '$MSYSTEM'
Invalid terminal: msyss2
$ echo "$?"
1
See my answer: How to detect the OS from a Bash script?
Example code to run gcc build commands on both Windows and Linux:
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
# For Linux, run normally
"./build_and_run_tests.sh"
elif [[ "$OSTYPE" == "msys" ]]; then
# For Windows, force running the tests inside the MSYS2 UCRT64 terminal
# where we have previously installed gcc with:
#
# ```bash
# # Upgrade all packages
# pacman -Syu
#
# # Install the gcc compiler
# pacman -S mingw-w64-ucrt-x86_64-gcc
# ```
#
msys2_terminal ucrt64 "./build_and_run_tests.sh"
fi
if [ "$?" -ne "0" ]; then
echo "Test FAILED."
fi
# ...
And that's all I know about this topic...
Upvotes: 0
Reputation: 146
Another way of doing it is using the msys2_shell.cmd
launcher on MSYS2' installation folder. Assuming that it was installed to the default folder, the command goes:
C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -here -c "your command here"
The option -defterm
sends the output to the current terminal, -no-start
makes so a new terminal window is not opened, -here
makes the current directory as the working directory, -mingw64
is the environment to use (other possibilities are -mingw32
, -ucrt64
, -clang64
), and -c
is your shell command itself.
Here is an example msys2_shell.cmd
help menu as shown when run inside of the Git Bash MSYS2-based terminal:
$ /c/msys64/msys2_shell.cmd --help
Usage:
msys2_shell.cmd [options] [login shell parameters]
Options:
-mingw32 | -mingw64 | -ucrt64 | -clang64 | -msys[2] Set shell type
-defterm | -mintty | -conemu Set terminal type
-here Use current directory as working
directory
-where DIRECTORY Use specified DIRECTORY as working
directory
-[use-]full-path Use full current PATH variable
instead of trimming to minimal
-no-start Do not use "start" command and
return login shell resulting
errorcode as this batch file
resulting errorcode
-shell SHELL Set login shell
-help | --help | -? | /? Display this help and exit
Any parameter that cannot be treated as valid option and all
following parameters are passed as login shell command parameters.
Upvotes: 12
Reputation: 41
add an supplement to the above: if u want to the output of shell script
-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"
=========
Upvotes: 0
Reputation: 2979
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"
Upvotes: 8
Reputation: 87446
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.
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.
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).
Upvotes: 22