I am trying to set an environment variable for Bash. However, I need this to be set before any of the shell's startup scripts (including /etc/profile), because /etc/profile acts differently based on the value of this variable.
Specifically, I want to create a shortcut to MinTTy that works like git-bash, but I need to set the MSYSTEM environment variable before the shell starts, or at least before it starts processing any startup scripts.
A solution that has MinTTy setting the environment variable before it starts the shell will also be accepted.
Edit:
What I am really looking for is sort of a command-line option to BASH that will set an environment variable, somewhat akin to the -D option to most C (and other) compilers. This would be a "general case" solution. Alternatively, a similar option (command line or configuration) to MinTTy will also do the job.
For my specific need, I have an idea for a potential work-around: Run a BASH script - with no startup scripts - that sets my required variable and execs another shell as a login shell.
Define the target of your shortcut file as follows:
C:\cygwin64\bin\mintty.exe /bin/bash -l -c "MSYSTEM=MINGW64 exec -l bash"
This command:
invokes bash directly as a login shell (-l)
passes it a command (-c) that defines the environment variable of interest (MSYSTEM=MINGW64) and then invokes a new copy of bash (exec -l bash), which inherits the existing environment, plus the new definition, but sources the profile(s) again, due to -l
(and prepends - to the executable name reported in $0 (-bash), as would happen if you started Mintty with just -, which is what the regular Cygwin64 Terminal shortcut does).
An alternative is to set the environment variable in Windows first.
[Not an option for the OP] If the environment variable should always have the same value, set it persistently as follows: run sysdm.cpl, go to the Advanced tab, click on Environment Variables... and define variable MSYSTEM as needed.
To define the variable ad-hoc, create a batch file as follows and make the shortcut target that batch file:
#echo off
# Define the env. variable with the desired value.
set "MSYSTEM=MINGW64"
# Invoke Mintty with a login shell, which will now see the env. variable.
# Adjust the path to mintty.exe as needed.
c:\cygwin64\bin\mintty.exe -
Note: Opening the batch file from a shortcut briefly opens a regular console window before opening Mintty, which may be undesired.
A simple helper WSH script, as demonstrated in this answer of mine, can prevent this.
You should just be able to do the same as you do in command prompt. Therefore, you can do:
set VAR=VarContents
Although I already accepted an answer above, I found this link that specifically addresses the second part of my question (Mintty specific) or an alternative way of setting an environment variable before running a command.
The contents of the Windows shortcut can be:
C:\cygwin64\bin\mintty.exe -t "Title" /bin/env "MSYSTEM=MINGW64" /bin/bash -l
(Suggested by Mintty Tips:Setting environment variables.)
Related
I've searched for an answer and read about different ways to do that in Mac but some of them are not relevant for Mojave or just didn't work for me.
I need to set Environment variable in terminal (bash), run script that creates processes, and I would like those processes to know the value of those environment variables.
How can I do that?
btw - writing export ENV_NAME=ENV_VAL in .bashrc or in .bash_profile didn't work.
Works for me. Have you RTFM? For example, ~/.bashrc is only read by interactive shells, not shell scripts. And ~/.bash_profile is only read by login shells. Again, shell scripts don't usually use the -l flag that would make them login shells. Also, if you put an export VAR=value statement in your ~/.bashrc it won't affect your current interactive shell. You need to start a new shell; e.g., by typing exec bash. Once you do that you should find the env var is defined. And it will be inherited by any process, including a shell script, you launch from that interactive session.
Note that if you run your script via crontab, for example, then you'll need a different means of setting the env var. For example, by using the --init-file flag or the BASH_ENV env var.
Can I have certain settings that are universal for all my users?
As well as /etc/profile which others have mentioned, some Linux systems now use a directory /etc/profile.d/; any .sh files in there will be sourced by /etc/profile. It's slightly neater to keep your custom environment stuff in these files than to just edit /etc/profile.
If your LinuxOS has this file:
/etc/environment
You can use it to permanently set environmental variables for all users.
Extracted from: http://www.sysadmit.com/2016/04/linux-variables-de-entorno-permanentes.html
man 8 pam_env
man 5 pam_env.conf
If all login services use PAM, and all login services have session required pam_env.so in their respective /etc/pam.d/* configuration files, then all login sessions will have some environment variables set as specified in pam_env's configuration file.
On most modern Linux distributions, this is all there by default -- just add your desired global environment variables to /etc/security/pam_env.conf.
This works regardless of the user's shell, and works for graphical logins too (if xdm/kdm/gdm/entrance/… is set up like this).
Amazingly, Unix and Linux do not actually have a place to set global environment variables. The best you can do is arrange for any specific shell to have a site-specific initialization.
If you put it in /etc/profile, that will take care of things for most posix-compatible shell users. This is probably "good enough" for non-critical purposes.
But anyone with a csh or tcsh shell won't see it, and I don't believe csh has a global initialization file.
Some interesting excerpts from the bash manpage:
When bash is invoked as an interactive
login shell, or as a non-interactive
shell with the --login option, it
first reads and executes commands from
the file /etc/profile, if that file
exists. After reading that file, it
looks for ~/.bash_profile,
~/.bash_login, and ~/.profile, in that
order, and reads and executes commands
from the first one that exists and is
readable. The --noprofile option may
be used when the shell is started to
inhibit this behavior.
...
When an
interactive shell that is not a login
shell is started, bash reads and
executes commands from
/etc/bash.bashrc and ~/.bashrc, if
these files exist. This may be
inhibited by using the --norc option.
The --rcfile file option will force
bash to read and execute commands from
file instead of /etc/bash.bashrc and
~/.bashrc.
So have a look at /etc/profile or /etc/bash.bashrc, these files are the right places for global settings. Put something like this in them to set up an environement variable:
export MY_VAR=xxx
Every process running under the Linux kernel receives its own, unique environment that it inherits from its parent. In this case, the parent will be either a shell itself (spawning a sub shell), or the 'login' program (on a typical system).
As each process' environment is protected, there is no way to 'inject' an environmental variable to every running process, so even if you modify the default shell .rc / profile, it won't go into effect until each process exits and reloads its start up settings.
Look in /etc/ to modify the default start up variables for any particular shell. Just realize that users can (and often do) change them in their individual settings.
Unix is designed to obey the user, within limits.
NB: Bash is not the only shell on your system. Pay careful attention to what the /bin/sh symbolic link actually points to. On many systems, this could actually be dash which is (by default, with no special invocation) POSIXLY correct. Therefore, you should take care to modify both defaults, or scripts that start with /bin/sh will not inherit your global defaults. Similarly, take care to avoid syntax that only bash understands when editing both, aka avoiding bashisms.
Using PAM is execellent.
# modify the display PAM
$ cat /etc/security/pam_env.conf
# BEFORE: $ export DISPLAY=:0.0 && python /var/tmp/myproject/click.py &
# AFTER : $ python $abc/click.py &
DISPLAY DEFAULT=${REMOTEHOST}:0.0 OVERRIDE=${DISPLAY}
abc DEFAULT=/var/tmp/myproject
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"
=========
If I write #/bin/bash at the top of a script and call this script from a web based application, will this load the .bashrc file on the machine where shell script is located?
The bash manual describes which startup files are read under which conditions. From it you would glean that bash reads .bashrc automatically only when invoked as an interactive, non-login shell, with neither the --norc option nor an --rcfile option naming a different file.
Note, however:
In addition to its default criteria for considering itself "interactive", bash considers itself interactive if invoked with the -i option. You can use this to cause ~/.bashrc to be read at startup when bash is invoked to run a script.
A non-interactive shell can be made to read a particular startup file (which can, in turn, read others) by specifying its name as the value of variable BASH_ENV in the shell's initial environment.
If it is read automatically, it is the user's ~/.bashrc that is read -- this is a characteristic of the user running the script, not of the machine overall.
No. .bashrc are relevant to interactive shells only.
In native emacs on windows, how can I specify environment variables for launching my shell inside emacs without modifying emacs' environment? In my specific case I'd like to set HOME to a cygwin-specific value for zsh without modifying where emacs thinks it's config file lives.
I've tried some things like changing my shell to env -u HOME ...\zsh.exe, but that seems to break (shell-command) (it appeared to involve argument order).
If this command existed, it would probably do what I want:
(setq explicit-zsh-environment '("HOME" nil))
I've read a bunch of related questions like (How can I run Cygwin Bash Shell from within Emacs?), but the unusual part for me is that all my config files are cygwin-ln-ed or windows-mklink-ed into a git repo and cygwin and windows take very different and incompatible approaches to symlinks.
Is this about running zsh as a shell inside Emacs (i.e. not about starting Emacs from a zsh shell), and having the environment that the inferior zsh process sees be different to the environment that Emacs has?
If so, you can bind the C-hv process-environment variable when you start a process. e.g.:
(let ((process-environment '("HOME=/tmp")))
(call-interactively 'shell))
$ echo $HOME
/tmp
From the Emacs manual:
Emacs sends the new shell the contents of the file ~/.emacs_shellname as input, if it exists, where shellname is the name of the file that the shell was loaded from. For example, if you use bash, the file sent to it is ~/.emacs_bash. If this file is not found, Emacs tries with ~/.emacs.d/init_shellname.sh.
So for zsh you would put inside ~/.emacs.d/init_zsh.sh something like:
export HOME=/tmp