# UNIX Shell befehl über JavaCode ausführen



## Basti91 (6. Sep 2011)

Hi, 
wie im Titel schon erwähnt, möchte ich über Java Befehle auf der UNIX Shell ausführen.
Auf Windows funktioniert mein Code jedoch wollte ich ihn auf UNIX umportieren und habe entsprechende Änderungen getroffen, leider ohne Erfolg.

Mein UNIX Code:

```
ProcessBuilder builder = new ProcessBuilder( "bash", "/c","COMMAND"); 
		try {
			Process p = builder.start();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
```


Mein Windows Code:

```
ProcessBuilder builder = new ProcessBuilder( "cmd", "/c","COMMAND"); 
		try {
			Process p = builder.start();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
```

Anscheinend muss es an dem Ersetzen vom "cmd" durch "bash" liegen jedoch habe ich nur Referenzen gefunden die "bash" benutzen.
Noch zu erwähnen ist vllt, dass ich ein Mavenkommando ausführen will also plattformunabhängig.


----------



## Ark (6. Sep 2011)

Unix bzw. unixoide Systeme funktionieren ganz anders. 

Unter Windows kommt mit cmd ein hässliches Fenster daher. Dieser Befehl ist unter Windows auch noch ziemlich eindeutig. Zum Ausführen von Befehlen ist cmd aber nicht notwendig!

Unter Unixoiden funktioniert das alles komplett anders. Eine Shell (z.B. bash) macht noch kein Fenster, und außerdem gibt es nicht nur bash, sondern auch noch csh, ksh, sh, … Wenn du ein Fenster benötigst (im Sinne von: du musst sysouts live verfolgen können oder über sysin was eingeben), wird die Sache noch komplizierter, denn es gibt nicht nur "den" Terminalemulator (wie bei Windows cmd), sondern z.B. gnome-terminal, konsole, urxvt, xterm, roxterm …

Brauchst du dieses Fenster auch unter Unixoiden? Wenn nicht: Führe einfach [c]COMMAND[/c] aus. Wenn doch, könnte es leichter sein, wenn der Benutzer selbst das Terminal startet und den Befehl ausführt.

Was genau soll denn letztendlich passieren? Was ist wichtig? Und wie genau hast du das unter Windows gelöst? Dann kann man dir eventuell auch sagen, wie so etwas normalerweise bei Unixoiden abläuft.

Ark


----------



## Basti91 (6. Sep 2011)

Ich habe ein Programm zur  Unterstüzung von Maven für Laien gebaut.
Durch Angabe von Pfad, Name und Version wird so die pom.xml erstellt und automatisch das Maven Projekt gebuildet.
Daher möchte ich den Maven Build-Befehl ausführen.


----------



## Ark (6. Sep 2011)

Basti91 hat gesagt.:


> Daher möchte ich den Maven Build-Befehl ausführen.


Spricht was dagegen, einfach den Befehl auszuführen?

```
ProcessBuilder builder = new ProcessBuilder( "mvn", "sonstige", "argumente"); 
try {
    Process p = builder.start();
} 
catch (IOException e) {
    e.printStackTrace();
}
```

Okay, unter Windows kommt dann kein Fenster mehr (sofern vorher eines kam), aber wenn das ein Problem sein sollte, kriegt man das wahrscheinlich auch ganz anders (und vor allem plattformunabhängiger) gelöst.

Ark


----------



## Asgar13 (6. Sep 2011)

```
ProcessBuilder builder = new ProcessBuilder("/bin/bash", "-c","COMMAND"); 
try {
    Process p = builder.start();
} 
catch (IOException e) {
    e.printStackTrace();
}
```

Funktioniert bei meinen Linux, also aus 
	
	
	
	





```
/bash
```
 zu 
	
	
	
	





```
/bin/bash
```
machen und
	
	
	
	





```
/c
```
 zu 
	
	
	
	





```
-c
```


----------



## Ark (6. Sep 2011)

@Asgar13: Und was ist, wenn statt bash eine andere Shell verwendet wird? Oder wenn bash nicht in [c]/bin[/c] liegt? Und (an den Threadstarter): warum überhaupt eine Shell?

Ark


----------



## Basti91 (6. Sep 2011)

Ich bin ein UNIX Neuling und weiss daher nicht was ich sonst verwenden sollte.
Für jegliche Vorschläge wie ich das Problem sonst lösen könnte bin ich daher natürlich offen


----------



## TheDarkRose (6. Sep 2011)

Ark hat es eh schon vorgeschlagen. 
	
	
	
	





```
ProcessBuilder builder = new ProcessBuilder( "mvn", "sonstige", "argumente");
```

Du brauchst keinen Umweg über CMD oder Bash gehen, da der ProccessBuilder die Kommandos von selbst in der jeweiligen Shell ausführt.


----------



## Basti91 (6. Sep 2011)

Mein Processbuilder sieht nun so aus:


```
ProcessBuilder builder = new ProcessBuilder( "mvn","mvn archetype:create -DgroupId=GROUPID -DartifactId="ARTIFACTID);
```

Jedoch bei der Ausführung kein Erfolg


----------



## Ark (6. Sep 2011)

Du musst jedes Argument einzeln aufzählen, ungefähr so:

```
ProcessBuilder builder = new ProcessBuilder( "mvn","archetype:create", "-DgroupId=GROUPID", "-DartifactId=ARTIFACTID");
```
Ark


----------



## Basti91 (6. Sep 2011)

Auch das bringt leider nicht das gewünschte Ergebnis.


----------



## bygones (6. Sep 2011)

Basti91 hat gesagt.:


> Ich habe ein Programm zur  Unterstüzung von Maven für Laien gebaut.
> Durch Angabe von Pfad, Name und Version wird so die pom.xml erstellt und automatisch das Maven Projekt gebuildet.
> Daher möchte ich den Maven Build-Befehl ausführen.


dann mach daraus ein Maven Plugin und der nutzer soll selbst dann "mvn bastisPlugin" ausfuehren....


----------



## Basti91 (6. Sep 2011)

@bygones: das kommt mir aber für den User zu umständlich vor. Wenn es doch unter Windows funktioniert muss es doch auch unter UNIX eine ähnliche Lösung geben


----------



## Ark (6. Sep 2011)

Basti91 hat gesagt.:


> Auch das bringt leider nicht das gewünschte Ergebnis.


Na, dann:


Ark hat gesagt.:


> Was genau soll denn letztendlich passieren? Was ist wichtig? Und wie genau hast du das unter Windows gelöst? Dann kann man dir eventuell auch sagen, wie so etwas normalerweise bei Unixoiden abläuft.


Ark


----------



## Basti91 (7. Sep 2011)

Letztendlich gibt der User Name, Pfad und Version des Projekts an woraus die pom.xml Dateien dynamisch erstellt werden. 
Daraufhin soll der User einfach das Builden des Maven Projekts anstoßen wodurch das Kommando im Hintergrund zusammengebaut und ausgeführt wird.
Anschließend soll dann noch die Möglichkeit bestehen das Projekt per Sonar zu reviewen (selbes Kommandoproblem).

Unter Windows habe ich die Kommandoausführung wie im ersten Post beschrieben gehandhabt.
Jedoch hatte sich da keine Kommandozeile geöffnet sondern der Befehl wurde einfach ausgeführt.
Daher hatte ich unter Windows auch während der Ausführung die cmd ausgelesen und in einer TextArea ausgegeben.


Hier nochmal der Windows Code: 

```
ProcessBuilder builder = new ProcessBuilder( "cmd","/c","archetype:create -DgroupId=GROUPID -DartifactId=ARTIFACTID");
        try {
            Process p = builder.start();
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
```


----------



## Effad (7. Sep 2011)

Mal direkt aus einem Projekt von mir, das kannst evtl. auf deine Bedürfnisse anpassen.
Wenn du extern ein Programm ausführst, musst du die Ausgaben (stdout, stderr) "abfangen" und irgendwie verarbeiten. Wichtig! pb.redirectErrorStream(true), sonst kriegst stderr nicht.

In meinem Fall logg ich die Dinger einfach raus (LOGGER.verbose)


```
private void bash(String cmd, File cwd) throws MigratorException {
		if (System.getProperty("os.name").startsWith("Windows")) 	return;
		try {
			ProcessBuilder pb = new ProcessBuilder("bash", "-c", cmd);
			if (cwd != null) {
				pb.directory(cwd);				
			}
			pb.redirectErrorStream(true);
			Process pr = pb.start();
			BufferedReader input = new BufferedReader(new InputStreamReader(pr.getInputStream()));
			
			String line=null;
			
			while((line=input.readLine()) != null) {
				LOGGER.verbose("bash>" + line);
			}
			
			int exitVal = pr.waitFor();
			if (exitVal != 0) {
				throw new Error("Failure while executing bash command '" + cmd + "'. Return code = " + exitVal); 
			}
		}
		catch (Exception e) {
			throw new Error("Could not execute bash command '" + cmd + "'.", e);
		}
	}
```


----------



## Basti91 (9. Sep 2011)

Ich muss mich entschuldigen, der vorschlag von Ark den mvn-Befehl einfach zu splitten funktioniert doch.

Hier mein Code:

```
public String generate_maven_project(){
		String cmd_output="";
	    ProcessBuilder builder = new ProcessBuilder( "mvn","archetype:create","-DgroupId=GROUPID","-DartifactId="+ARTIFACTID); //executing the command to build maven project"
	    builder.directory( new File(path) ); 

		try {
			Process p = builder.start();
		    Scanner s = new Scanner( p.getInputStream() ).useDelimiter( "," ); ; 
		    cmd_output =s.next().toString();
		} 
		catch (IOException e) {
			e.printStackTrace();
		} 
		return cmd_output;
	  }
```

Die Methode führ das Builden eines maven Projektes durch, liest die Shell aus und gibt sie im String cmd_output zurück (cmd_output da es zuerst ein Windowsprogramm war).
Ein kleines Problem habe ich hier jedoch noch: Unter Windows habe ich als Delimiter "//Z" benutzt was auch wunderbar funktioner hat.
Leider funktioniert "//Z" unter UNIX garnicht weshalb ich nach kurzer Recherche "," verwende.
Bei "," wird nun aber nicht die ganze Shell ausgelesen sondern nur ca 15 Zeilen.
Weiss jemand was ich als Delmiter benutzten kann um die gesamte Shell auszulesen?


----------



## bygones (9. Sep 2011)

Basti91 hat gesagt.:


> @bygones: das kommt mir aber für den User zu umständlich vor. Wenn es doch unter Windows funktioniert muss es doch auch unter UNIX eine ähnliche Lösung geben



wie bitte ?

was soll fuer den user denn so schwer sein "mvn bastisplugin" einzugeben ?

mach ein shellscript mit diesem Befehl drin, dann eben.

Ist meiner Ansicht nach absoluter Unsinn Maven in irgendein Programm so umzubiegen....


----------



## Basti91 (9. Sep 2011)

Das Programm erstellt die pom.xml automatisch, kann daraus ein Maven Projekt erstellen und dann auch Sonar über das Maven Projekt laufen lassen.
Das alles in einen Programm erscheint mir für Benutzer die nichts mit der Shell am Hut haben, aber trotzdem Code reviewen wollen schon nützlich, bzw für Shell-Kundige ein schnellerer Weg.


----------



## Ark (9. Sep 2011)

Basti91 hat gesagt.:


> Leider funktioniert "//Z" unter UNIX garnicht weshalb ich nach kurzer Recherche "," verwende.
> Bei "," wird nun aber nicht die ganze Shell ausgelesen sondern nur ca 15 Zeilen.
> Weiss jemand was ich als Delmiter benutzten kann um die gesamte Shell auszulesen?


Da ich kein Maven verwende, kann ich leider nicht einmal erahnen, was genau du damit bezwecken willst. Was bringt es denn, die komplette Ausgabe zu puffern? Und was hast du ausgerechnet mit dem Komma vor? Ich vermute mal, dass in deinem Fall die Ausgabe genau vor dem ersten Komma, das dann hätte kommen sollen, aufgehört hat. 

Wenn du mehr haben willst, musst du [c]Scanner.next()[/c] entsprechend oft aufrufen. Ein Scanner ist aber eigentlich dazu da, tokenweise einzulesen. Wenn du einfach nur "alles" haben willst, ist ein Scanner wahrscheinlich die falsche Wahl. Effads Vorschlag solltest du dir da auch einmal genauer ansehen (da wird nämlich stur zeilenweise vorgegangen). [c]LOGGER.verbose()[/c] kannst du da getrost durch etwas anderes ersetzen, wenn du magst, z.B. durch das Schreiben in eine Datei (oder halt Anhängen an einen StringBuilder, wenn es unbedingt sein muss).

Ark


----------

