0

Scenario

While executing a series of commands using the ProcessBuilder I noticed I am currently not able to set an environment variable such that it remains "known" after a set of commands has been executed.

Question

How do I recreate the effects* of the export TASKDDATA=/var/taskd command in a .jar file?

Attempt 0

ProcessBuilder environment variable in java Provides a way to set the environment variable for each specific command, but when I execute the .jar of that solution and check if the environment variable $u is still set after execution, I find it is not. Whereas $TASKDDATA does remain set after execution. To illustrate:

a@DESKTOP-desktopName:/mnt/e$ echo $TASKDDATA

a@DESKTOP-desktopName:/mnt/e$ TASKDDATA=/var/taskd
a@DESKTOP-desktopName:/mnt/e$ echo $TASKDDATA
/var/taskd
a@DESKTOP-desktopName:/mnt/e$ sudo java -jar autoInstallTaskwarrior.jar
[sudo] password for a:
Process ended with rc=0

Standard Output:

util/


Standard Error:


a@DESKTOP-desktopName:/mnt/e$ echo $TASKDDATA
/var/taskd
a@DESKTOP-desktopName:/mnt/e$ echo $u

Attempt 1

For a single command the environment variable can be using the solution I wrote in: Java ProcessBuilder how to get binary output from command. However that does not conserve the taskvariable for a second command in which it needs to be set again. However, when the export command is used, the environment variable is not required to be set again. Specifically this difference comes to light when the java code is finished and the user wants to enter any additional commands that require the environment variable. In that case, the user needs to first type the export command again.

Attempt 2

An additional difference occurs when a new shell is opened to acquire root priviliges with sudo -s. Not only does setting the environment within the .jar file require setting it again for every separate command but additionally the environment variable is not passsed along to the new shell with root priviliges. For example executing the following commands:

commandLines[53] = new String[4];
commandLines[53][0] = "sudo";
commandLines[53][1] = "-s";
commandLines[53][2] = "taskdctl"; 
commandLines[53][3] = "start";
commands[53].setCommandLines(commandLines[53]);
commands[53].setEnvVarContent("/var/taskd");
commands[53].setEnvVarName("TASKDDATA");
commands[53].setWorkingPath("/usr/share/taskd/pki");

commandLines[54] = new String[5];
commandLines[54][0] = "sudo";
commandLines[54][1] = "-s";
commandLines[54][2] = "task"; 
commandLines[54][3] = "sync";
commandLines[54][4] = "init";
commands[54].setCommandLines(commandLines[54]);
commands[54].setEnvVarContent("/var/taskd");
commands[54].setEnvVarName("TASKDDATA");
commands[54].setWorkingPath("/usr/share/taskd/pki");

returns:

53RUNNINGCOMMAND=sudo -s taskdctl start
The TASKDDATA variable must be set.

54RUNNINGCOMMAND=sudo -s task sync
Could not connect to 0.0.0.0 53589
Sync failed.  Could not connect to the Taskserver.
Syncing with 0.0.0.0:53589

Note 1

Setting the environment variable $TASKDDATA=/var/taskd before executing the .jar with: TASKDDATA=/var/taskd sudo java -jar autoInstallTaskwarrior.jar does not ensure the environment variable $TASKDDATA remains available after the execution of the .jar file. Furthermore it is beyond the scope of the question as it is not set within the .jar file but outside the .jar file.

Note 2

I understood it is not appropriate to use the export command as a command that is executed by the processbuilder, much like the cd command.

*That is why the question focuses on reproducing the "longterm/lasting" effect/availability of setting the environment variable, rather than "how to execute the export command.".

a.t.
  • 2,002
  • 3
  • 26
  • 66

1 Answers1

1

when I execute the .jar of that solution and check if the environment variable $u is still set after execution, I find it is not.

That is the expected behavior. Some operating systems support the concept of global "environment" variables. But not UNIX. In UNIX like operating systems every process has its own private copy of environment vars. A process cannot modify the environment of another process. This is also why changing the current working directory in a process does not change the cwd of its parent process. Each process has its own cwd.

The usual way to workaround that limitation is for the child process to write the var=val pairs to stdout and the parent shell then evaluates that output to set the vars in its environment. For illustration assume the command is the following shell script, named myscript.sh, rather than a Java program:

#!/bin/sh
echo VAR_A=val_a
echo VAR_B=val_b

Then the parent shell does

export $(./myscript.sh)
Kurtis Rader
  • 6,734
  • 13
  • 20
  • Thank you for your explanation on why it is expected behavior. In your answer I missed how it is doable from within a `.jar` file. Am I to derive from your answer that either: 0. It is not possible solely using a single `.jar` file? 1. It might be possible but this is my closest approximation (XY-problem solved but not from within a `.jar` file)? 2. You implied-, but I misunderstood how your solution functions from within a `.jar` file. I will attempt to run the `export $(./myscript.sh)` as a command from within a `.jar` file to test your implementation. Assuming that is what you intended. – a.t. Apr 23 '19 at 09:52
  • The implementation I thought was intended yielded output: `56RUNNINGCOMMAND=export $(./myscript.sh) java.io.IOException: Cannot run program "export" (in directory "/mnt/e/somefolder"): error=2, No such file or directory`. (I first ran `chmod +x myscript.sh` and verfied `myscript.sh` is located in `/mnt/e/somefolder`.) So `export` is seen as a program rather than the syntax of shell. So I currently do not understand how your answer is implemented in a `.jar` file. Any elaboration would be greatly appreciated! – a.t. Apr 23 '19 at 10:35
  • @a.t. `export` is not a program. It is a shell builtin command. Your jar program cannot modify the environment of the parent shell without the cooperation of that shell. The solution is for your `.jar` program to write the `var=value` pairs to its stdout stream and have the parent shell export those into its environment. My *myscript.sh* example was only a trivial example of how to do it. Whether the program being run in a `.jar` file, a shell script, or a program written in some other language is completely irrelevant. – Kurtis Rader Apr 23 '19 at 20:17