Environment: mac
Precondition: I already configured 'adb' full path to my bash_profile. and when I tried type 'adb' in my terminal, it is working.
But, I tried to exec 'adb' command from java, 'adb' is not working, instead I need to pass the full adb path to make it work.
I guess this is probably something to do with the bash_profile setting, anyone know the exact reason for this issue?
Runtime.getRuntime().exec() runs /bin/sh -c <command>. If this is or points to a bash shell on your system: A non-interactive bash does not read .bash_profile unless explicitly (--login) told to do so.
From the documentation:
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.
It's a little convoluted, but non-interactive, non-login bash instances don't read the profile files.
Your path settings does not get picked up by the subshell (which is actually /bin/sh which might not even be bash at all).
If you want, you can add the path to adb system wide by adding an appropriate entry to to /etc/paths.d.
Related
I have azure-cli installed in my mac and I can successfully execute all azure commands from cli. I want to do the same from a java application. I have seen examples of using REST endpoints and azure-sdk-for-java. But what I want is to execute the azure-cli commands directly from java.
I tried running 'azure login' from a java program, but I got the following error.
Cannot run program "azure": error=2, No such file or directory
The problem is in a lack of your PATH variable. If you want to enjoy the same "find the command" capabilities that you have on the command line, you need to set it somehow within your Java program: import the PATH variable from the environment, set it within the code you're writing, or whatever mixture covers your needs.
Giving the absolute (full) path is the surest way, but you may not want to read such long command names in your source codes.
#harshithabt Per my experience, the issue was caused by the command azure could not be searched in the directories listed in PATH. There are two ways to solve the issue for running commands in Java, please see below.
Setting up the environment variable PATH for the current shell session or the configuration files ~/.profile(or ~/.bashrc).
If you command export PATH=<your-node-path>/bin:$PATH in a shell session, you only run the Java program with azure command in the current shell session, even run via the Java IDE which must be opened in the current shell session (it means you should open Eclipse via command <your-ide-path>/eclipse).
If you configure the files ~/.profile or ~/bashrc to add the node runtime as below, please make sure the configuration files have been reloaded via command source ~/.profile in a current session or restart sessions via logout & login or reboot.
A simple way is that adding the command links into the dirs listed in PATH, such as /bin. You just need to run the commands below.
sudo ln -s /bin/node /bin/node
sudo ln -s /bin/azure /bin/azure
Then you can run azure-cli command from Java successfully.
I used to use Debian's alternatives system to set 'global env' like java, javac, javap but I've read about the disadvantages.
So I added
export JAVA_HOME=/opt/jdk/java
export PATH=$JAVA_HOME/bin:$PATH
to my ~/.bashrc and when I open my terminal I can use the commands as expected but my most of my shell scripts doesn't work anymore.
As you can see in the picture below they check if $JAVA_HOME exists and executes the following command which does nothing. When I enter $JAVA_HOME/bin/java -version it works correctly. If I start the script in a terminal it works, too.
So it seems that #!/bin/sh doesn't source .bashrc? Changing it to #!/bin/bash doesn't solve the problem.
I tried to add the export commands to /etc/profile but this doesn't seem to get sourced at startup/login.
Does anyone have an idea or keywords? I think the solution is quite simple but at the moment I'm stuck.
Thank you in advance!
UPDATE:
Starting the script in the bash terminal with ./something.sh works fine. Right click and execute or 'Open with bash' (XFCE4 context menu) does nothing.
Bash loads .bashrc only when the shell is interactive and non-login. In your case the shell is not interactive, so .bashrc is not loaded.
.bashrc contains a check that prevents it from executing if the shell is not interactive. Usually the first thing .bashrc does is:
case $- in
*i*) ;;
*) return;;
esac
This will prevent you from calling source .bashrc from your script.
The script should inherit from your parent shell, so you should be getting all variable that have been exported before you ran the script.
Also, the preferred shebang is #!/usr/bin/env bash which is more portable.
So in your case:
Open a terminal window. That will load .bashrc, but just to make sure, run . .bashrc and then echo $JAVA_HOME to verify that the variable was set correctly.
Then your script will simply be:
#!/usr/bin/env bash
java -jar <whatever>
If you have some other script related variable that you want to set, you can do that by sourcing a "settings" script:
#!/usr/bin/env bash
source ~/settings.sh
echo $SOME_VAR_SET_IN_SETTINGS_SH
Adding additional folders to your PATH variable in XFCE4 with LightDM will not work as expected! Shell scripts that use these additional commands and are started from the graphical environment will fail because the can't find the command. Logging the PATH variable while starting one of these scripts shows that the PATH variable is overwritten with the very default one. Why? Because LightDM hardcodes the PATH variable and overwrites it for the graphical environment. Screw you! Look here!
Source: https://ljwo.wordpress.com/2014/02/02/global-path-in-debian-wheezy-xfce/
Disable it or use another DM.
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
I've used a number of different *nix-based systems of the years, and it seems like every flavor of Bash I use has a different algorithm for deciding which startup scripts to run. For the purposes of tasks like setting up environment variables and aliases and printing startup messages (e.g. MOTDs), which startup script is the appropriate place to do these?
What's the difference between putting things in .bashrc, .bash_profile, and .environment? I've also seen other files such as .login, .bash_login, and .profile; are these ever relevant? What are the differences in which ones get run when logging in physically, logging in remotely via ssh, and opening a new terminal window? Are there any significant differences across platforms (including Mac OS X (and its Terminal.app) and Cygwin Bash)?
The main difference with shell config files is that some are only read by "login" shells (eg. when you login from another host, or login at the text console of a local unix machine). these are the ones called, say, .login or .profile or .zlogin (depending on which shell you're using).
Then you have config files that are read by "interactive" shells (as in, ones connected to a terminal (or pseudo-terminal in the case of, say, a terminal emulator running under a windowing system). these are the ones with names like .bashrc, .tcshrc, .zshrc, etc.
bash complicates this in that .bashrc is only read by a shell that's both interactive and non-login, so you'll find most people end up telling their .bash_profile to also read .bashrc with something like
[[ -r ~/.bashrc ]] && . ~/.bashrc
Other shells behave differently - eg with zsh, .zshrc is always read for an interactive shell, whether it's a login one or not.
The manual page for bash explains the circumstances under which each file is read. Yes, behaviour is generally consistent between machines.
.profile is simply the login script filename originally used by /bin/sh. bash, being generally backwards-compatible with /bin/sh, will read .profile if one exists.
That's simple. It's explained in man bash:
/bin/bash
The bash executable
/etc/profile
The systemwide initialization file, executed for login shells
~/.bash_profile
The personal initialization file, executed for login shells
~/.bashrc
The individual per-interactive-shell startup file
~/.bash_logout
The individual login shell cleanup file, executed when a login shell exits
~/.inputrc
Individual readline initialization file
Login shells are the ones that are read one you login (so, they are not executed when merely starting up xterm, for example). There are other ways to login. For example using an X display manager. Those have other ways to read and export environment variables at login time.
Also read the INVOCATION chapter in the manual. It says "The following paragraphs describe how bash executes its startup files.", i think that's a spot-on :) It explains what an "interactive" shell is too.
Bash does not know about .environment. I suspect that's a file of your distribution, to set environment variables independent of the shell that you drive.
Classically, ~/.profile is used by Bourne Shell, and is probably supported by Bash as a legacy measure. Again, ~/.login and ~/.cshrc were used by C Shell - I'm not sure that Bash uses them at all.
The ~/.bash_profile would be used once, at login. The ~/.bashrc script is read every time a shell is started. This is analogous to /.cshrc for C Shell.
One consequence is that stuff in ~/.bashrc should be as lightweight (minimal) as possible to reduce the overhead when starting a non-login shell.
I believe the ~/.environment file is a compatibility file for Korn Shell.
I found information about .bashrc and .bash_profile here to sum it up:
.bash_profile is executed when you
login. Stuff you put in there might be
your PATH and other important
environment variables.
.bashrc is used for non login shells.
I'm not sure what that means. I know
that RedHat
executes it everytime you start
another shell (su to this user or
simply calling bash again) You might
want to put aliases in there but again
I am not sure what that means. I
simply ignore it myself.
.profile is the equivalent of
.bash_profile for the root. I think
the name is changed to let other
shells (csh, sh, tcsh) use it as well.
(you don't need one as a user)
There is also .bash_logout wich
executes at, yeah good guess...logout.
You might want to stop deamons or even
make a little housekeeping . You can
also add "clear" there if you want to
clear the screen when you log out.
Also there is a complete follow up on each of the configurations files here
These are probably even distro.-dependant, not all distros choose to have each configuraton with them and some have even more. But when they have the same name, they usualy include the same content.
According to Josh Staiger, Mac OS X's Terminal.app actually runs a login shell rather than a non-login shell by default for each new terminal window, calling .bash_profile instead of .bashrc.
He recommends:
Most of the time you don’t want to maintain two separate config files
for login and non-login shells — when you set a PATH, you want it to
apply to both. You can fix this by sourcing .bashrc from your
.bash_profile file, then putting PATH and common settings in .bashrc.
To do this, add the following lines to .bash_profile:
if [ -f ~/.bashrc ]; then
source ~/.bashrc
fi
Now when you login to your
machine from a console .bashrc will be called.
A good place to look at is the man page of bash. Here's an online version. Look for "INVOCATION" section.
I have used Debian-family distros which appear to execute .profile, but not .bash_profile,
whereas RHEL derivatives execute .bash_profile before .profile.
It seems to be a mess when you have to set up environment variables to work in any Linux OS.
I'm trying to write Java code that executes some terminal commands. The code should execute this command sudo mount -o loop system.img system. But there are several problems. First, to execute this command I have to be root. I know that I can be by sudo su, but how can I stay as root when I close the terminal window? If I use the command sudo mount -o loop system.img system how can I provide the password in the Java code?
The second issue is: can I execute the command as below?
File f2 = new File("/home/user1/Desktop/aDirectory");
String[] commands = new String[]{"sudo mount", "-o", "loop", "/home/user1/Desktop/aDirectory/system.img"};
Runtime.getRuntime().exec(commands, null, f2);
I think I can't. So how can I do it? Any ideas?
Notes: system.img is a compiled Android os file. and the system is an empty directory. The thing I'm trying to do is mount the system.img file into the system directory.
Programs like sudo read the password directly from the terminal device, not from stdin, so this is unfortunately not a trivial thing to do. I'm not sure if this is realistic for Android or not, but on a general UNIX system the easiest solution is to use expect, which is a library for simulating a terminal and thereby automating these kinds of interactions. It's often used as a standalone program embedded in Tcl, and I've thrown together systems in which Java launched expect to talk to tools like sudo, and it works fine.
expect includes a sort of declarative scripting language that tells it how to run another program and how to react to that program's output.
What you would do is use Runtime.exec() to execute the expect program, supplying a script that just runs "sudo mount", watches for the password prompt, and provides the password. The script would probably just look something like (G4rb4geB4rg3 is the password):
spawn sudo mount -o loop /home/user1/Desktop/aDirectory/system.img
expect "password:"
send "G4rb4geB4rg3\r"
expect eof
The problem was solved, by using shell script.
I wrote a script includes just this line :
echo myPassword | sudo -S mount -o loop system.img system
then I run it in my java code, such :
Runtime.getRuntime().exec("sh 1.sh");
I'm pretty sure 'sudo' and 'mount' would be separate, since it's not a single executable you're invoking. Also, if you start sudo with the -S command line switch it can take the password directly from stdin, so you just need to start the process and pass in whatever the password's configured as to the input stream.
I have a java application and I want to run a script whenever it experiences and OutOfMemoryException
This works great:
$ java -server -XX:OnOutOfMemoryError="./oom_script %p" TestOOMClass
Unfortunately my application is run by a bash script in production. The script boils down to this:
cmd='java -server -XX:OnOutOfMemoryError="./oom_script %p" TestOOMClass'
##does a lot of checking and cmd building here
exec -a app ${cmd}
When run like this java never respects the double quotes and thinks %p is the class. how do I prevent this? I've tried double escaping but that doesn't work.
Since your program is run as a shell script, I would suggest putting this as the first line in your shell script after the shebang:
set -xv
Then, in the crontab, put 2>&1 at the end of the command line, so STDERR and STDOUT are merged. Crontab usually emails out the STDOUT of a command to root, so you can see what the output is. If not, then apend the following to the end of the command in your crontab:
> /somedir/output.$$ 2>&1
Make sure somedir exists, and after crontab runs your command, you'll see the verbose and debug output. Each line in your shell script will be displayed before it is executed -- both as written and as the shell actually interprets it.
The set -xv becomes very useful in debugging any sell script. There could be all sorts of environmental issues involved between the cronjob and the script running under your login. You might even find a shell issue. For example, crontab usually executes shell scripts in Bourne shell and you probably have Bash or Kornshell as your default shell. Whatever it is, you'll usually find out the issue very quickly when you turn on verbose/debug mode.
You don't even have to do this to the entire script. You can put set -xv anywhere in your script to turn on verbose/debug mode, and set +xv to turn it off.
I could make several pious high minded recommendations (use quotes, don't assume environment things, prefix your command line with "bash -c" to make sure you're using the right shell, etc.), but this would be guessing what could be wrong. In order to really debug this issue, I would need to see the machine, know the OS, see your entire shell script, and understand the entire environment. And, the first thing I would do is add set -xv in your shell script.
Quotes and escaping is an art. I would suggest you add echo ${cmd} before calling exec so you can see what it looks like then.
I would suggest using
cmd='java -server -XX:OnOutOfMemoryError=\\"./oom_script %p\\" TestOOMClass'
instead (untested). You need it to look like \" when being echoed.
an alternative i suggest (to bypass the problem, not solve it indeed) is to rung and bash script and access the $PPID:
PPID The process ID of the shell's parent. This variable is readonly.
then kill the process with that ID (please bare in mind that is an untested suggestion)