2017-07-28 4 views
1

J'ai un projet Java qui aide à trouver toutes les informations de vidéos dans la playlist youtube en utilisant youtube-dl. Voici le Main.javajava block lors de l'exécution de la commande shell en utilisant le processus

import java.io.*; 


public class Main { 

public static void main(String[] args) throws Exception { 
    String command1 = "/usr/local/bin/youtube-dl --flat-playlist --dump-single-json https://www.youtube.com/playlist?list=PLFcOH1YaRqUo1yEjY5ly09RFbIpUePF7G"; 
    String command2 = "/usr/local/bin/youtube-dl --flat-playlist --dump-single-json https://www.youtube.com/playlist?list=PLC6A0625DCA9AAE2D"; 
    System.out.println(executeCommand(command1)); 

} 

private static String executeCommand(String command) throws IOException, InterruptedException { 
    int exitCode = 0; 
    String result = ""; 
    Process process; 
    ProcessBuilder builder = new ProcessBuilder(command.replaceAll("[ ]+", " ").split(" ")); 
    builder.directory(new File("/tmp/test")); 
    process = builder.start(); 
    exitCode = process.waitFor(); 
    return getStringFromInputStream(process.getInputStream()); 
} 
private static String getStringFromInputStream(InputStream is) { 

    BufferedReader br = null; 
    StringBuilder sb = new StringBuilder(); 

    String line; 
    try { 

     br = new BufferedReader(new InputStreamReader(is)); 
     while ((line = br.readLine()) != null) { 
      sb.append(line); 
     } 

    } catch (IOException e) { 
     e.printStackTrace(); 
    } finally { 
     if (br != null) { 
      try { 
       br.close(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
    return sb.toString(); 
} 
} 

Le command1 et command2 sont identiques à l'exception du paramètre playlist Youbute. La playlist de command2 a 409 vidéos et a 200 vidéos dans command1. Je peux obtenir le résultat avec succès pour les deux commandes dans le terminal. Juste le command2 prend plus de temps, mais seulement quelques secondes. Mais quand je lance le Main.java (javac Main.java; java Main), pour command1 il imprime le résultat avec succès, mais pour command2 il se bloque là pendant plusieurs minutes sans n'importe quel résultat. Voici le jstack pour ce processus

"main" #1 prio=5 os_prio=0 tid=0x00007f828c009800 nid=0xce7 in Object.wait() [0x00007f8293cf7000] 
java.lang.Thread.State: WAITING (on object monitor) 
    at java.lang.Object.wait(Native Method) 
    - waiting on <0x0000000771df9fe0> (a java.lang.UNIXProcess) 
    at java.lang.Object.wait(Object.java:502) 
    at java.lang.UNIXProcess.waitFor(UNIXProcess.java:396) 
    - locked <0x0000000771df9fe0> (a java.lang.UNIXProcess) 
    at Main.executeCommand(Main.java:18) 
    at Main.main(Main.java:8) 

Il se bloque à exitCode = process.waitFor();. Je n'en ai aucune idée. Quelqu'un peut-il m'aider? Merci beaucoup.

+0

Avez-vous essayé d'ajouter quelques points d'arrêt dans votre EDI et de parcourir le code pendant qu'il s'exécute pour voir où il se trouve? –

+1

@KevinHooke Je m'attend à exitCode = process.waitFor(); Je viens de mettre à jour la question et ajouté le jstack. – user2256235

+0

Définissez un point d'arrêt et prenez la ligne de commande analysée (après le remplacer tout), puis exécutez-le dans shell et voir si cela fonctionne. Peut-être que la commande finale rend mal dans votre code. – zuckermanori

Répondre

1

Comme mentionné dans les commentaires, la sortie du sous-processus est par défaut envoyée à un canal qui peut être lu en utilisant Process.getInputStream(). Si le sous-processus génère beaucoup de sortie et que le programme Java ne le consomme pas, le tampon du tube se remplira et le sous-processus se bloquera lors de l'écriture.

La solution la plus simple consiste à appeler le .inheritIO() sur ProcessBuilder. Cela enverra la sortie à la console au lieu de la mettre en mémoire tampon (même chose pour les flux d'entrée et d'erreur).