2016-11-03 4 views
0

Ich möchte überprüfen, ob ein Verzeichnis fehlerhafte Links enthält und die Nummer zurückgeben. Ich möchte den Befehl find -L /dir/* -type d -prune -o -type l | wc -l verwenden und es funktioniert gut in meinem Terminal. Aber wenn ich es in meinem Java-Code über runtime.exec laufen, wirft er einen Fehler:Wie bash cmd "find -L" innerhalb Java-Code aufrufen

Unable to call process "find -L /dir/* -type d -prune -o -type l | wc -l ". Cause: find: paths must precede expression: |Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]

Ich versuchte '/dar/*', "/dir/*" und /dir/\*, aber keiner von ihnen arbeitet. Jede Hilfe wird geschätzt.

+0

@andlrc zu finden: unbekannt Prädikat ' -L ' – user2038560

+0

Warum verwenden Sie einen externen Befehl, um etwas zu tun, was einfacher Java-Code kann? – VGR

+0

@VGR Dies ist ein altes Projekt und es hat bereits viele externe Befehle verwendet. Ich möchte den Code konsistent halten. Aber wenn ich keine Lösung finden kann, wechsle ich zu Java Plain Solution. – user2038560

Antwort

0

Der Pipe-Operator (|) ist eine Funktionalität, die von einer Shell bereitgestellt wird, wie bash, nicht durch den Befehl find. Runtime.exec übergibt nur alle Argumente an den Befehl, der durch das erste Wort der Zeichenfolge definiert wird - in diesem Fall find - und der Befehl find hat keine Ahnung, was dieses Pipe-Symbol ist.

Sie können diesen Effekt für sich selbst sehen, indem Sie den Befehl in einem regulären Terminalfenster ausgeführt wird, aber mit den | als \| so entging es den find Befehl übergeben wird, wie Ihr Java Code tun:

find -L /dir/* -type d -prune -o -type l \| wc -l 

eine übliche Lösung besteht darin, den gesamten Befehl als Argument o bash -c zu passieren:

Process process = new ProcessBuilder("bash", "-c", 
    "find -L /dir/* -type d -prune -o -type l | wc -l").start(); 

(Runtime.exec ist veraltet, dessen Ersatz ist Process, die mehr hat cl . Anfang definiertes Verhalten)

Eine bessere Lösung ist es, die Linien in Java zu zählen, und vermeiden Sie das Pipe-Symbol zusammen:

Process process = new ProcessBuilder(
    "find", "-L", "/dir/*", "-type", "d", "-prune", "-o", "-type", "l").start(); 

long brokenLinkCount; 
try (BufferedReader lineReader = 
    new BufferedReader(new InputStreamReader(process.getInputStream()))) { 

    brokenLinkCount = lineReader.lines().count(); 
} 

jedes Argument Passing einzeln Process hat auch den Vorteil, dass der Befehl für funktioniert alle Pfade, auch solche mit Leerzeichen oder Anführungszeichen im Dateinamen.

Schließlich, auch wenn Sie gesagt haben, Sie nicht wollen, diesen Weg zu gehen, ist der beste Ansatz mit externen Kommandos und tut es in Java zu tun, weg:

Path dir = Paths.get("/dir"); 

Stream<Path> brokenLinks = 
    Files.find(dir, 1, (path, attr) -> attr.isSymbolicLink()) 
    .filter(path -> { 
     try { 
      return !Files.exists(Files.readSymbolicLink(path)); 
     } catch (IOException e) { 
      System.err.println("Couldn't read link " + path + ": " + e); 
      return false; 
     } 
    }); 
long brokenLinkCount = brokenLinks.count();