מדריך ל- java.lang.ProcessBuilder API

1. סקירה כללית

ה- API של התהליך מספק דרך עוצמתית לבצע פקודות של מערכת הפעלה בג'אווה. עם זאת, יש לו כמה אפשרויות שיכולות לגרום לזה להיות מסורבל לעבוד איתו.

במדריך זה, אנו נסתכל כיצד ג'אווה מקל על כך באמצעות ProcessBuilder ממשק API.

2. ProcessBuilder ממשק API

ה ProcessBuilder class מספק שיטות ליצירה והגדרת תצורה של תהליכי מערכת הפעלה. כל אחד ProcessBuilder מופע מאפשר לנו לנהל אוסף של תכונות תהליך. נוכל להתחיל חדש תהליך עם התכונות הנתונות.

להלן מספר תרחישים נפוצים שבהם נוכל להשתמש ב- API זה:

  • מצא את גרסת Java הנוכחית
  • הגדר מפת ערך-מפתח מותאמת אישית עבור הסביבה שלנו
  • שנה את ספריית העבודה שבה פועלת פקודת הפגז שלנו
  • הפנה זרמי קלט ופלט לתחליפים מותאמים אישית
  • ירש את שני הזרמים של תהליך ה- JVM הנוכחי
  • בצע פקודת מעטפת מקוד Java

אנו נסתכל על דוגמאות מעשיות לכל אחד מהם בסעיפים מאוחרים יותר.

אך לפני שנצלול לקוד העבודה, בואו נבדוק איזה סוג פונקציונליות ה- API הזה מספק.

2.1. סיכום השיטה

בחלק הזה, אנו נלך צעד אחורה ונבחן בקצרה את השיטות החשובות ביותר ב ProcessBuilder מעמד. זה יעזור לנו כשאנחנו צוללים לכמה דוגמאות אמיתיות בהמשך:

  • ProcessBuilder (מחרוזת ... פקודה)

    כדי ליצור בונה תהליכים חדש עם תוכנית מערכת ההפעלה והארגומנטים שצוינו, נוכל להשתמש בבנאי הנוח הזה.

  • ספרייה (ספריית קבצים)

    אנו יכולים לעקוף את ספריית העבודה המוגדרת כברירת מחדל של התהליך הנוכחי על ידי קריאה ל- מַדרִיך שיטה והעברת a קוֹבֶץ לְהִתְנַגֵד. כברירת מחדל, ספריית העבודה הנוכחית מוגדרת לערך שהוחזר על ידי ה- user.dir נכס מערכת.

  • סביבה()

    אם אנו רוצים לקבל את משתני הסביבה הנוכחיים, אנו יכולים פשוט להתקשר ל- סביבה שיטה. זה מחזיר לנו עותק של סביבת התהליך הנוכחית באמצעות System.getenv () אלא כ מַפָּה .

  • inheritIO ()

    אם ברצוננו לציין שהמקור והיעד עבור קלט / פלט הסטנדרטי של תהליך המשנה שלנו צריכים להיות זהים לזה של תהליך ה- Java הנוכחי, אנו יכולים להשתמש ב ירושה שיטה.

  • redirectInput (קובץ קובץ), redirectOutput (קובץ קובץ), redirectError (קובץ קובץ)

    כאשר אנו רוצים להפנות את הקלט, הפלט והשגיאה הסטנדרטיים של בונה התהליכים לקובץ, עומדות לרשותנו שלוש שיטות ההפניה הדומות.

  • הַתחָלָה()

    אחרון חביב, כדי להתחיל בתהליך חדש עם מה שהגדרנו, אנחנו פשוט קוראים הַתחָלָה().

עלינו לציין כי מחלקה זו אינה מסונכרנת. לדוגמא, אם יש לנו מספר שרשורים שניגשים אל ProcessBuilder מופע במקביל ואז יש לנהל את הסנכרון באופן חיצוני.

3. דוגמאות

עכשיו שיש לנו הבנה בסיסית של ה- ProcessBuilder API, בואו נעבור כמה דוגמאות.

3.1. באמצעות ProcessBuilder להדפסת גרסת Java

בדוגמה ראשונה זו, נפעיל את ה- ג'אווה פקודה עם טיעון אחד על מנת לקבל את הגרסה.

תהליך תהליך = ProcessBuilder חדש ("java", "-version"). התחל ();

ראשית, אנו יוצרים את שלנו ProcessBuilder אובייקט שמעביר את ערכי הפקודה והוויכוח לבנאי. לאחר מכן, אנו מתחילים את התהליך באמצעות ה- הַתחָלָה() שיטה להשיג א תהליך לְהִתְנַגֵד.

בואו נראה כיצד לטפל בפלט:

תוצאות רשימה = readOutput (process.getInputStream ()); assertThat ("התוצאות לא צריכות להיות ריקות", תוצאות, זה (לא (ריק ()))); assertThat ("התוצאות צריכות להכיל גרסת Java:", results, hasItem (containString ("גרסת Java"))); int exitCode = process.waitFor (); assertEquals ("אין לזהות שגיאות", 0, exitCode);

כאן אנו קוראים את תפוקת התהליך ואימות התוכן הוא כפי שאנו מצפים לו. בשלב הסופי, אנו מחכים לסיום השימוש בתהליך process.waitFor ().

לאחר סיום התהליך, ערך ההחזר אומר לנו אם התהליך הצליח או לא.

כמה נקודות חשובות שיש לזכור:

  • הטענות חייבות להיות בסדר הנכון
  • יתר על כן, בדוגמה זו משתמשים בספריית העבודה והסביבה המוגדרים כברירת מחדל
  • בכוונה אנחנו לא מתקשרים process.waitFor () עד אחרי שקראנו את הפלט מכיוון שמאגר הפלט עשוי לעצור את התהליך
  • הנחנו שה- ג'אווה הפקודה זמינה דרך נָתִיב מִשְׁתַנֶה

3.2. התחלת תהליך עם סביבה שונה

בדוגמה הבאה זו, נראה כיצד לשנות את סביבת העבודה.

אבל לפני שנעשה את זה נתחיל במבט על סוג המידע שאנחנו יכולים למצוא בסביבת ברירת המחדל:

ProcessBuilder processBuilder = ProcessBuilder חדש (); סביבת מפה = processBuilder.environment (); environment.forEach ((מפתח, ערך) -> System.out.println (מפתח + ערך));

זה פשוט מדפיס את כל אחד מהערכים המשתנים המסופקים כברירת מחדל:

PATH / usr / bin: / bin: / usr / sbin: / sbin SHELL / bin / bash ...

כעת אנו מוסיפים משתנה סביבה חדש למצב שלנו ProcessBuilder התנגד והפעל פקודה להפקת הערך שלה:

environment.put ("GREETING", "Hola Mundo"); processBuilder.command ("/ bin / bash", "-c", "הד $ GREETING"); תהליך תהליך = processBuilder.start ();

בואו נפרק את השלבים להבנת מה שעשינו:

  • הוסף משתנה הנקרא 'GREETING' עם הערך 'Hola Mundo' לסביבה שלנו שהוא סטנדרט מַפָּה
  • הפעם, במקום להשתמש בבנאי, הגדרנו את הפקודה והוויכוחים באמצעות ה- פקודה (מחרוזת ... פקודה) שיטה באופן ישיר.
  • לאחר מכן אנו מתחילים את התהליך שלנו לפי הדוגמה הקודמת.

להשלמת הדוגמה, אנו מוודאים שהפלט מכיל את הברכה שלנו:

תוצאות רשימה = readOutput (process.getInputStream ()); assertThat ("התוצאות לא צריכות להיות ריקות", תוצאות, זה (לא (ריק ()))); assertThat ("התוצאות צריכות להכיל גרסת Java:", results, hasItem (containString ("Hola Mundo")));

3.3. התחלת תהליך עם מדריך עבודה שונה

לפעמים זה יכול להיות שימושי לשנות את ספריית העבודה. בדוגמה הבאה שלנו נראה כיצד לעשות בדיוק את זה:

@Test הציבור בטל givenProcessBuilder_whenModifyWorkingDir_thenSuccess () זורק IOException, InterruptedException {ProcessBuilder processBuilder = ProcessBuilder חדש ("/ bin / sh", "-c", "ls"); processBuilder.directory (קובץ חדש ("src")); תהליך תהליך = processBuilder.start (); תוצאות רשימה = readOutput (process.getInputStream ()); assertThat ("התוצאות לא צריכות להיות ריקות", תוצאות, זה (לא (ריק ()))); assertThat ("התוצאות צריכות להכיל רשימת ספריות:", תוצאות, מכיל ("ראשי", "מבחן")); int exitCode = process.waitFor (); assertEquals ("אין לזהות שגיאות", 0, exitCode); }

בדוגמה שלעיל, הגדרנו את ספריית העבודה לזו של הפרויקט src דיר בשיטת הנוחות ספרייה (ספריית קבצים). לאחר מכן אנו מריצים פקודת רישום ספריות פשוטה ובודקים שהפלט מכיל את ספריות המשנה רָאשִׁי ו מִבְחָן.

3.4. הפניית קלט ופלט סטנדרטיים

בעולם האמיתי, כנראה שנרצה לתפוס את התוצאות של תהליכי הריצה שלנו בתוך קובץ יומן לצורך ניתוח נוסף. למרבה המזל ה ProcessBuilder ל- API יש תמיכה מובנית בדיוק לזה כפי שנראה בדוגמה זו.

כברירת מחדל, התהליך שלנו קורא קלט מצינור. אנו יכולים לגשת לצינור זה באמצעות זרם הפלט שהוחזר על ידי Process.getOutputStream ().

עם זאת, כפי שנראה בקרוב, הפלט הסטנדרטי עשוי להיות מנותב למקור אחר כגון קובץ בשיטה redirectOutput. במקרה הזה, getOutputStream () יחזיר א ProcessBuilder.NullOutputStream.

נחזור לדוגמא המקורית שלנו כדי להדפיס את גרסת Java. אבל הפעם בואו להפנות מחדש את הפלט לקובץ יומן במקום לצינור הפלט הסטנדרטי:

ProcessBuilder processBuilder = ProcessBuilder חדש ("java", "-version"); processBuilder.redirectErrorStream (נכון); יומן קבצים = folder.newFile ("java-version.log"); processBuilder.redirectOutput (יומן); תהליך תהליך = processBuilder.start ();

בדוגמה שלעיל, אנו יוצרים קובץ זמני חדש שנקרא יומן ומספרים לנו ProcessBuilder להפניית פלט ליעד קובץ זה.

בקטע האחרון האחרון, אנו פשוט בודקים זאת getInputStream () הוא אכן ריק וכי תוכן הקובץ שלנו הוא כצפוי:

assertEquals ("אם מנותב מחדש, צריך להיות -1", -1, process.getInputStream (). read ()); רשימת שורות = Files.lines (log.toPath ()). Collect (Collectors.toList ()); assertThat ("התוצאות צריכות להכיל גרסת Java:", שורות, hasItem (containString ("גרסת Java")));

בואו נסתכל על וריאציה קלה בדוגמה זו. למשל כשאנחנו רוצים להוסיף לקובץ יומן ולא ליצור חדש בכל פעם:

יומן קבצים = tempFolder.newFile ("java-version-append.log"); processBuilder.redirectErrorStream (נכון); processBuilder.redirectOutput (Redirect.appendTo (יומן));

חשוב גם להזכיר את השיחה redirectErrorStream (נכון). במקרה של שגיאות, פלט השגיאות יתמזג לקובץ פלט התהליך הרגיל.

אנו יכולים כמובן לציין קבצים בודדים עבור הפלט הסטנדרטי ופלט השגיאה הסטנדרטי:

קובץ outputLog = tempFolder.newFile ("standard-output.log"); קובץ errorLog = tempFolder.newFile ("error.log"); processBuilder.redirectOutput (Redirect.appendTo (outputLog)); processBuilder.redirectError (Redirect.appendTo (errorLog));

3.5. ירושה של קלט / פלט התהליך הנוכחי

בדוגמה הלפני אחרונה, נראה את ירושה () שיטה בפעולה. אנו יכולים להשתמש בשיטה זו כאשר אנו רוצים להפנות את קלט / פלט התהליך המשנה לקלט / פלט הסטנדרטי של התהליך הנוכחי:

@Test ציבורי בטל givenProcessBuilder_whenInheritIO_thenSuccess () זורק IOException, InterruptedException {ProcessBuilder processBuilder = ProcessBuilder חדש ("/ bin / sh", "-c", "הד שלום"); processBuilder.inheritIO (); תהליך תהליך = processBuilder.start (); int exitCode = process.waitFor (); assertEquals ("אין לזהות שגיאות", 0, exitCode); }

בדוגמה שלעיל, באמצעות inheritIO () שיטה אנו רואים את הפלט של פקודה פשוטה במסוף ב- IDE שלנו.

בחלק הבא, נבחן אילו תוספות בוצעו ל ProcessBuilder ממשק API ב- Java 9.

4. תוספות Java 9

ג'אווה 9 הציגה את הרעיון של צינורות ל- ProcessBuilder ממשק API:

רשימת התחלה ציבורית סטטית ציבורית (בניית רשימה) 

משתמש ב startPipeline שיטה שנוכל להעביר רשימה של ProcessBuilder חפצים. שיטה סטטית זו תתחיל אז תהליך לכל אחד ProcessBuilder. כך, יצירת צינור של תהליכים המקושרים על ידי הפלט הסטנדרטי שלהם וזרמי הקלט הסטנדרטיים שלהם.

לדוגמה, אם אנו רוצים להפעיל משהו כזה:

למצוא . -שם * .java -סוג f | wc -l

מה שהיינו עושים הוא ליצור בונה תהליכים עבור כל פקודה מבודדת ולחבר אותם לצינור:

@Test ציבורי בטל givenProcessBuilder_whenStartingPipeline_thenSuccess () זורק IOException, InterruptedException {בוני רשימה = Arrays.asList (חדש ProcessBuilder ("למצוא", "src", "-שם", "* .java", "-סוג", "f") , ProcessBuilder חדש ("wc", "-l")); תהליכי רשימה = ProcessBuilder.startPipeline (בוני); תהליך אחרון = processs.get (processs.size () - 1); פלט רשימה = readOutput (last.getInputStream ()); assertThat ("התוצאות לא צריכות להיות ריקות", פלט, הוא (לא (ריק ()))); }

בדוגמה זו אנו מחפשים את כל קבצי ה- Java בתוך ה- src ספרייה וצנרת התוצאות לתהליך אחר כדי לספור אותן.

כדי ללמוד על שיפורים אחרים שנעשו ב- API של התהליך ב- Java 9, עיין במאמר הנהדר שלנו בנושא שיפורים ב- Java 9 Process API.

5. מסקנה

לסיכום, במדריך זה חקרנו את java.lang.ProcessBuilder ממשק API בפירוט.

ראשית, התחלנו בהסבר מה ניתן לעשות עם ה- API וסיכמנו את השיטות החשובות ביותר.

לאחר מכן, הסתכלנו על מספר דוגמאות מעשיות. לבסוף בדקנו אילו תוספות חדשות הוצגו ל- API ב- Java 9.

כמו תמיד, קוד המקור המלא של המאמר זמין באתר GitHub.


$config[zx-auto] not found$config[zx-overlay] not found