es
Feedback
ToCode

ToCode

Ir al canal en Telegram

טיפים קצרים למתכנתים מאת ינון פרק

Mostrar más
1 419
Suscriptores
Sin datos24 horas
Sin datos7 días
+130 días
Archivo de publicaciones
ToCode
1 419
# טיפ קוברנטיס: שימוש בקונטקסט כדי לעבוד עם כמה קלאסטרים הטיפ הזה אמור היה להיות ממש פשוט אבל בגלל שאני הסתבכתי עם התיעוד שלהם כדי ללמוד אותו חשבתי שאולי גם לכם זה יעזור. המשחק הוא כזה- הכלי kubectl משתמש בקובץ קונפיגורציה ברירת מחדל שנמצא בנתיב ~/.kube/config. בתוך אותו קובץ יש הגדרות כמו לדוגמה:
apiVersion: v1
clusters:
- cluster:
    server: https://cimaks-dns-bca70ca9.hcp.westeurope.azmk8s.io:443
  name: cimaks
contexts:
- context:
    cluster: cimaks
    user: clusterUser_Playground_cimaks
  name: cimaks
current-context: cimaks
kind: Config
preferences: {}
אבל אנחנו יכולים לקבל קבצי הגדרות אחרים ממקומות אחרים. לדוגמה כשאני פותח חשבון באוקטטו אני מקבל מהם קובץ קונפיגורציה שנראה ככה:
apiVersion: v1
clusters:
- cluster:
    server: https://146.148.56.200:443
  name: cloud_okteto_com
contexts:
- context:
    cluster: cloud_okteto_com
    extensions:
    - extension: null
      name: okteto
    namespace: ynonp
    user: 6b5b39bc-9c83-4d7b-a345-b03922c7e979
  name: cloud_okteto_com
current-context: cloud_okteto_com
kind: Config
preferences: {}
שימו לב איך שכל קובץ קונפיגורציה פותח בבלוק clusters ואחריו בלוק contexts, ובתוך ה contexts יש הגדרה של קונטקסט והתיחסות לקלאסטר שהוגדר קודם. ולמה זה יפה? כי עכשיו אם נשלב שני קבצי קונפיגורציה יחד אז נקבל קובץ קונפיגורציה אחד ארוך שכולל גם את הגדרת הקלאסטר והקונטקסט מהקובץ הראשון, וגם את ההגדרה מהקובץ השני. תכל'ס לא חייבים לשלב את שני הקבצים לקובץ אחד ואפשר להשתמש במשתנה סביבה KUBECONFIG בשביל להגיד ל kubectl לאחד את הקונפיגורציה בזיכרון. אני כותב את הנתיבים לשני הקבצים בתור תוכן משתנה הסביבה:
$ export KUBECONFIG=~/.kube/config:~/Downloads/okteto-kube.config
ועכשיו הפקודה:
$ kubectl config get-contexts
מחזירה לי את שני הקונטקסטים משני קבצי הקונפיגורציה, כלומר מדפיסה:
CURRENT   NAME               CLUSTER            AUTHINFO        NAMESPACE
*         cimaks             cimaks             
          cloud_okteto_com   cloud_okteto_com                   ynonp
מההדפסה אני רואה שפקודת ה kubectl הבאה שאכתוב תגיע לקלאסטר cimaks כי הוא מסומן בכוכבית. הפקודה use context בוחרת את הקונטקסט הפעיל ולכן אני יכול לשנות קלאסטר עם:
$ kubectl config use-context cloud_okteto_com
ועכשיו כל פקודת kubectl שאכתוב תפעל על הקלאסטר השני.

ToCode
1 419
# הפחד לכתוב גרוע לפני כמה ימים חבר תייג אותי בפוסט מעניין בלינקדאין. בדרך כלל אני לא עונה שם אבל הפעם היה קשה להתעלם. הפוסט דיבר על כתיבה יומית ועלתה שם שוב השאלה מה עושים כשאין לך על מה לכתוב. האם שווה רק "לשרבב כמה מילים" בשביל שיצא פוסט, או שעדיף לשתוק ולדלג על פוסט. הנה כמה ציטוטים מאותו דיון- "לא עדיף לכתוב רק שיש לך משהוא חשוב או מועיל?" "כאשר חסר בפוסט את הערך המוסף, הנזק רב מהתועלת." "כלל הזהב שלי הוא: אם אין לך מה להגיד - אל תגיד את זה כאן. או בכלל. ובניסוחם של חז"ל: מילה בסלע, שתיקה בשניים." --- עם גישה כזאת קשה לכתוב ובטח שקשה להתמיד בכתיבה. אבל, לכתיבה יכולה גם להיות מטרה מעבר לתפוצה של פוסט בודד. המטרה של הבלוג הזה היא לעזור לאנשים לכתוב קוד טוב יותר, לשאול שאלות מעניינות לגבי קוד ולגבש דעה ביקורתית על טרנדים בעולם התכנות. שינוי לא קורה ביום אחד. כמו המשפט הישן ש״אין הטיפה חוצבת בסלע מכח עוצמתה אלא מכוח התמדתה״ כך בשביל לעשות שינוי אנחנו צריכים את ההתמדה ואת התזכורת ואת החזרה. אני לא חושב שיש פוסט אחד שכתבתי שיגרום למישהו לשנות גישה, אבל אני בטוח שמי שיעקוב אחר הבלוג על בסיס יומי בסופו של דבר יראה את האפקט. וכמו שבכתיבת קוד אנחנו מוכנים לפעמים להכניס קוד גרוע למערכת בשביל להתקדם, כך גם בבלוג - לא כל הפוסטים יהיו באותה רמה. לא כל הפוסטים ידברו לכולם באותו אופן. אבל, גם אני לא השופט הכי טוב: יש פוסטים שהייתי בטוח שהם לא מספיק טובים וקיבלתי עליהם תגובות מעולות, ופוסטים אחרים שהייתי בטוח שהם מעבירים את המסר בצורה מדויקת ובסוף כמעט אף אחד לא הבין מה רציתי להגיד בהם. לא כדאי לפחד מעבודה גרועה. עדיף לעבוד ולשפר. או במילים הכל כך מדויקות של סת': > לאנשים עם מחסום כתיבה אין בעיה בהקלדה. יש להם בעיה לחיות עם כתיבה גרועה, כתיבה לא מושלמת, כתיבה שאולי תחשוף משהו ממנו הם פוחדים. הדרך להתמודד עם זה אינה לחכות שהכתיבה תהיה מושלמת, כי אם תחכו לעולם לא תגיעו לשם. > הדרך להתמודד היא לכתוב, ולהבין שלא מתים מכתיבה גרועה. כמו בכל מיומנות, אנחנו משתפרים עם אימון ופידבק.

ToCode
1 419
# טיפ ג'סט: ג'אסט אין קייס הספריה עם השם הקליט jest in case עוזרת לנו לכתוב בדיקות בהן צריך לבדוק את אותו דבר על כמה קלטים שונים. זה נראה ככה:
import cases from 'jest-in-case';

cases('find stuff in arrays', ({haystack, needle, found}) => {
  expect(haystack.includes(needle)).toEqual(found);
}, {
  '5 is in [2, 3, 5]': 
    { haystack: [2, 3, 5], needle: 5, found: true },

  '19 is not in [2, 3, 5]':
    { haystack: [2, 3, 5], needle: 19, found: false },
});
וכשאני מריץ ג'סט הפלט של הקוד יהיה:
PASS  ./demo.test.js
 find stuff in arrays
    ✓ 5 is in [2, 3, 5] (1 ms)
    ✓ 19 is not in [2, 3, 5]

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.102 s
Ran all test suites.
מה קרה כאן- 1. הפקודה cases מקבלת שם של "קבוצת" בדיקות, אחריה פונקציית בדיקה (כמו test רגיל) אבל אותה פונקציית בדיקה הפעם מקבלת אוביקט פרמטרים. 2. אתם יכולים לבחור כל שם שתרצו למפתחות באוביקט הפרמטרים (מלבד כמה מילים שמורות של jest in case), ולהשתמש בהם בתוך קוד הבדיקה. 3. הפרמטר האחרון ל cases הוא אוביקט של מקרים בו המפתח הוא תיאור המקרה והערך הוא אוביקט עם ערכים, שייכנס בתור פרמטר לפונקציית הבדיקה. 4. שימו לב ל Destructuring בהגדרת פונקציית הבדיקה. הפונקציה מקבלת אוביקט ואני מוציא ממנו את המפתחות שמעניינים אותי כבר בחתימה כדי שיהיה לי קל להשתמש בהם בקוד הבדיקה. הספריה לא חדשה. העדכון האחרון שלה מלפני שנתיים, דף הבית שלה בעיצוב מיושן והגדרות הטיפוסים המובנות בה כתובות ב flow במקום בטייפסקריפט. מצד שני אין הרבה קוד והיא עדיין עובדת טוב, ואפשר למצוא את הגדרות ה typescript שמתאימות לה ב Definitely Typed.

ToCode
1 419
# לירות לכל הכיוונים אם את מחפשת עבודה יכול להיות שבחודש הראשון עוד יש לך את הסבלנות לשלוח קורות חיים רק למשרות רלוונטיות, ולתקן את הקורות חיים לכל משרה. אבל אחרי חודש או חודשיים של חיפושים מתחילים להרגיש את הפיתוי לירות לכל הכיוונים - כלומר, לשלוח קורות חיים לכמה שיותר משרות, לשלוח לכמה שיותר אנשים הודעות, לוותר על ציפיות שכר או תנאים רק בשביל לקצר את החיפוש. הייאוש יוצר לחץ והלחץ דורש מעשים. זה לא אפקטיבי. כי הבעיה עם לירות לכל הכיוונים היא שאם מראש אין לך מספיק אנרגיה בשביל למצוא עבודה אחת, אז לכוון ליותר מטרות רק מקטין את כמות המשאבים שאת שמה על כניסה לעבודה ספציפית. זה קצת דומה למוכר ארטיקים שימכור בחוף הים ארטיק במחיר הפסד, ויחשוב שמה שהוא צריך בשביל להרוויח זה לעבוד יותר שעות. יותר כדאי קודם להבין איך למכור יחידה אחת ברווח (גם אם זה לוקח זמן), ורק אחר כך אם זה עדיין רלוונטי להתחיל להרחיב את החיפוש.

ToCode
1 419
# מי צריך לכתוב את ה Dockerfile? המעבר לדוקר יצר הפרדת כוחות בריאה בין מפתחים ל Devops. המפתחים אחראים על כתיבת הקוד וה Devops אחראים על העברה של הקוד לפרודקשן, הרצה ומעקב אחריו. נקודות המפגש בין שתי הקבוצות היא ה Dockerfile. לכאורה דוקרפייל קשור להתקנות ולמה צריך להיות על השרת בשביל להריץ את האפליקציה, ולכן להרבה אנשים נדמה שה Devops צריכים לכתוב אותו. למרות שבהרבה חברות זה גם מה שקורה, אני חושב שזו טעות. הפרדת כוחות בריאה בין מתכנתים ל Devops, כזאת שעוזרת לשתי הקבוצות, צריכה לאפשר לכל קבוצה לעשות את העבודה שלה בצורה הטובה ביותר. רק המתכנתים שכותבים את הקוד יודעים איזה גירסה של ספריה בדיוק צריכים להתקין, ואם אפשר לעבור לספריה אחרת כדי לקבל אימג' יותר קטן. רק מתכנתים שכותבים את הקוד יודעים להחליט איזה חלק בלוגיקה ילך לשלב בניית האימג' ואיזה חלק לעלייה של הקונטיינר. רק מתכנתים שכותבים את הקוד יודעים להחליט איזה חלקים במערכת יכולים לרוץ בתור קונטיינרים נפרדים, ומה צריך לרוץ בתוך אותו קונטיינר. איזה כלים נוספים הקוד צריך ומתי צריך לשדרג ספריות ישנות. זאת בדיוק העבודה שלנו. בצד של ה Devops, כשאנשי אופרציה מקבלים Dockerfile הם מקבלים מוצר מוגמר. הם יכולים לנטר את הדבר שהוא מריץ, להחליט כמה משאבים לתת לו, להוסיף עוד עותקים מקונטיינרים עמוסים, ובקיצור לדאוג לעבודה חלקה של כל המערכת. הם לא צריכים לדאוג לתוכן של אותו קונטיינר - הקונטיינר, בדיוק כמו קונטיינר של ספינות - הוא אטום. מתכנתים בונים אותו ואופרציה מעבירים אותו ודואגים שירוץ על מכונה אחרת. ברגע שאנחנו כותבים את ה Dockerfile ועובדים עם אותו דוקרפייל גם בפיתוח, אנחנו לא נכנסים לפינות של "אצלי זה עובד" ומתחילים לחפש מה התקינו על הדוקרפייל שלא היה אצלי על המחשב. המהפכה של דוקר אומרת שדוקרפייל הוא חלק מהאפליקציה. ורק אם נאמץ אותה נוכל ליהנות מכל מה שיש לשיטת העבודה החדשה להציע.

ToCode
1 419
# איך לבדוק פונקציות שאינן מיוצאות? שאלתי לא מזמן את גוגל איך לבדוק פונקציות ב JavaScript שאינן מיוצאות. גוגל כמו גוגל שלח אותי לתשובה מאוד מקיפה שכוללת שימוש במודול rewire, שמתחבר לבייבל ומאפשר למשוך פונקציה שאינה מיוצאת לסביבת בדיקות. המדריך כאן אתם יכולים לנסות לקרוא אותו: https://www.samanthaming.com/journal/2-testing-non-exported-functions/#proficiency-leads-to-result ליישם זה כבר סיפור אחר. מנגנוני בניה לפרויקטי פרונט-אנד הפכו כל כך מורכבים בשנים האחרונות, שגם אם תצליחו להוסיף את rewire לפרויקט שלכם אף אחד לא מבטיח שהוא ימשיך לעבוד בשידרוג הבא של בייבל או וובפאק או מה שלא יהיה. יותר קל אני חושב לקחת צעד אחורה. "איך לבדוק פונקציה שאינה מיוצאת" אתם שואלים? והתשובה שלי - או שתיצאו אותה, או שלא תבדקו. הבחירה של JavaScript לתת לנו גישה בבדיקות רק לפונקציות מיוצאות היא שרירותית לגמרי. ב go אפשר לגשת לפונקציות פרטיות מאותה חבילה, ב python כל השמות מיוצאים בברירת מחדל וגם ברובי אפשר לגשת לכל משתנה פרטי לצורך בדיקות (גם אם תוך שימוש בקצת קסם אפל של השפה). ומצד שני אם אני משוכנע שפונקציה פרטית מסוימת צריכה בדיקות, אז יכול להיות שהיא לא כל כך פרטית ושאפשר יהיה להשתמש בה גם במקומות אחרים.

ToCode
1 419
# שלוש דרכים להתמודד עם סיבוכים כשקוד מתחיל להיות מסובך אפשר לעבוד על לעשות אותו פשוט. אפשר לפרק אותו, לארגן אותו אחרת; כשהממשק הופך להיות מסובך אפשר לבנות מנגנון לשימוש "קל" בספריה. אז לדוגמה ברובי יש לנו ספריית בדיקות בשם capybara שהיא פשוט ממשק קל יותר להפעלת סלניום. כתיבה של ספריה חדשה לגמרי כדי להפוך את העבודה עם סלניום לפשוטה זו עבודה קשה אבל התוצאה שווה את זה. אפשרות שניה להתמודד עם קוד מסובך היא להחליט לקחת צעד אחורה. הספריה שלי מסובכת מדי? אולי היא עושה יותר מדי וצריך להוריד ממנה יכולות. הבעיה מסובכת מדי? אולי צריך לפתור בעיה יותר פשוטה. אחד הדברים שהפכו את ריאקט לכזו הצלחה בהשוואה לכל הפריימוורקים שהיו לפניה הוא העובדה שריאקט פותרת בעיה יותר פשוטה ממה שכל הפריימוורקס האחרות ניסו לפתור. ריאקט היא "רק" שכבת התצוגה, בעוד שכל האחרות ניסו לתת כלים לפיתוח יישום צד לקוח מלא. ואפשרות שלישית והכי פחות כיפית היא להתעלם מהסיבוך או לשכנע את העולם שאין ברירה. יש פונקציה שמקבלת 15 פרמטרים? אין מה לעשות, היא עושה הרבה דברים. קשה לעבוד עם קוברניטס? אין מה לעשות מערכת הפעלה זה דבר מסובך. לאורך זמן גישה כזאת נוטה להפוך מערכות להרבה יותר מורכבות ממה שצריך, ואת המתכנתים להרבה פחות מאושרים ממה שאפשר. דוגמה עצובה במיוחד שנתקלתי בה היא Issue 9643 מספריית fluentui, שמסתיים במילים: > Due to the complexity and dependencies of our List components, we are not able to take new feature requests at this time. ואלה בדיוק התשובות שאנחנו לא רוצים לעולם לכתוב.

ToCode
1 419
# עדיף לא לבדוק בדיקות יחידה אמורות להפוך את החיים שלנו לקלים יותר ולתת לנו יותר ביטחון בעבודה על הקוד. אבל מה קורה כשבדיקה עושה בדיוק את ההיפך? איך קורה שבדיקה יכולה להיות ממש מזיקה? בואו נראה מתי עדיף לא לבדוק. ## הקוד נניח שיש לי פונקציה גדולה שמפעילה כמה פונקציות קטנות ומחברת את המידע שחוזר מכולן. חלק מהפונקציות משתמשות במידע שחושב עד עכשיו, ואחרות עצמאיות לגמרי. בקיצור משהו כזה:
async function f1() { return 5; }
async function f2() { return 9; }
async function f3() { return 2; }

async function complexStuff() {
    let x = 0;
    x += await f1();
    x += await f2();
    x += await f3();

    return x;
}
הקוד עבר ניקוי בשביל להתאים לפוסט, אבל דמיינו שבגירסה האמיתית הפונקציות f1, f2 ו f3 תופסות כמה עשרות שורות כל אחת של חישובים מסובכים ושליפות מ DB, והפונקציה complexStuff גם היא קוראת לכל מיני מקורות מידע חיצוניים ומחברת את הכל לתוצאה אחת. ## הבדיקה המזיקה אופציה אחת לבדוק קוד כזה היא לדרוס חלק מהפונקציות הפנימיות עם spies ואז לוודא רק את הלוגיקה של הפונקציה החיצונית, כלומר:
test('complexStuff', () => {
    jest.spyOn(util, 'f1').mockResolvedValue(5);
    jest.spyOn(util, 'f2').mockResolvedValue(9);
    jest.spyOn(util, 'f3').mockResolvedValue(2);
    
    expect(complexStuff()).resolves.toEqual(16);
});
וזאת בדיוק האופציה שאני ממליץ להיזהר ממנה. הסיבה שגישה זו מזיקה היא החיבור בין פונקציית הבדיקה למימוש הספציפי של הקוד. אם מחר בבוקר נרצה לשנות את הפונקציות הפרטיות f1, f2 ו f3 כך שלדוגמה f2 תהיה מיותרת וכל העבודה שלה תעבור ל f1, אז הריפקטור הזה ישבור את הבדיקה - אפילו אם הוא יבוצע בלי אף טעות. כשבמערכת מצטברות בדיקות שיישברו בכל ריפקטור, אנחנו נעשה פחות refactorings כי כל ארגון מחדש של הקוד דורש עכשיו לתקן גם את כל הבדיקות שהתבססו על המימוש הישן. לא רק שהבדיקות לא עזרו לנו להרגיש ביטחון ולארגן מחדש את הקוד כשהיינו צריכים, הן אפילו הזיקו, כי עכשיו הארגון מחדש דורש יותר עבודה. ## מה עושים במקום ברגע שאנחנו רואים את הצורך לנתק בין הבדיקה למימוש הספציפי אפשר להתחיל לחשוב על פיתרונות יותר טובים. בדוגמה שלנו אפשר לארגן מחדש את הקוד לפני כתיבת הבדיקות ולהפריד בין הלוגיקה הפנימית של complexStuff לשימוש שהיא עושה בשלושת פונקציות העזר. משהו בסגנון הזה:
async function f1() { return 5; }
async function f2() { return 9; }
async function f3() { return 2; }

async function sumHelpers(...helpers) {
    let x = 0;
    for (let f of helpers) {
        x += await f();
    }
    
    return x;
}

async function complexStuff() {
    return sumHelpers(f1, f2, f3);
}
עכשיו אני יכול לכתוב תוכנית בדיקה שתוודא ש sumHelpers עושה את מה שהיא הבטיחה שתעשה, ותוכניות בדיקה נפרדות ל f1, f2 ו f3, ולהשאיר את complexStuff בלי בדיקה. אם יהיה Refactor עתידי שבו פונקציה f2 תהפוך למיותרת, אני אעדכן את complexStuff כדי לא לקרוא לה, אבל בגלל שב complexStuff החדש אין את כל הלוגיקה וגם אין לו בדיקה, לא תהיה לי אף בדיקה שתישבר. הבדיקות יישברו רק אם באמת שיניתי משהו מהותי בהיגיון הפנימי של הפונקציות.

ToCode
1 419
המתג -d מאפשר לי לציין תו הפרדה בין רמות היררכיה במחרוזת המפתח. אני בחרתי נקודה ולכן jo מפצל את המפתח foo.bar.buz להיררכיה של שלושה אוביקטים, אחד בתוך השני. את אותו הגיון אני יכול להפעיל על רשימת התהליכים כדי לקבל בדיוק את האוביקט שרציתי. אני רק צריך להשתמש ב awk כדי להפוך את הרווח לנקודה:
ps -e -o ppid,pid,comm|tail +2 | awk '{print $1"."$2"="$3}'|jo -d.
## חיבור הגדרות מכל ה JSON-ים בתיקייה לקובץ אחד דוגמה אחרונה לפוסט משתמשת בעוד יכולת של jo שהיא קריאת חלק מאוביקט מקובץ. אם יש לי בתיקיה קובץ בשם a.json עם התוכן הבא:
{"window":"one","top":10,"left":20}
אז אני יכול להשתמש ב jo כדי לשתול את כל תוכן הקובץ בתור תת-עץ באוביקט שאני יוצר:
$ jo -p name=ynon data=:a.json

{
  "name": "ynon",
  "data": {
    "window": "one",
    "top": 10,
    "left": 20
  }
}
(נ.ב. שימו לב ל -p שגורם ל jo להדפיס את ה json מחולק לשורות ועם רווחים). אז עכשיו נדמיין שיש לנו מספר קבצי json בתיקיה ואנחנו רוצים לאחד את כל התוכן שלהם לקובץ json יחיד. בגלל שחלק מהמפתחות עלולים להיות כפולים, אני רוצה לשמר את ההיררכיה של התיקיה בתוך קובץ ה JSON. כלומר אם יש לי בתיקיה את a.json עם התוכן שדיברנו עליו, ובתת תיקיה בשם bar יש לי קובץ בשם b.json עם תוכן אחר, אז אני רוצה לקבל אוביקט json שנראה כך:
{
    a.json: { ... contents of file a.json },
    bar: {
        b.json: {
            ... contents of file b.json
        }
    }
}
איך בונים את הקובץ? עם jo כמובן. אני מחפש את כל קבצי ה json בתיקיה ובכל תתי התיקיות שלה עם find, ומדפיס את התוצאה עם סימן הנקודותיים:
$ find . -type f -name '*.json' -exec echo {}=:{} \;

./a.json=:./a.json
./bar/b.json=:./bar/b.json
עכשיו אני משלב את -d כדי ליצור אוביקטים מקוננים ואת הנקודותיים כדי לקרוא תוכן של קובץ ומקבל:
$ find . -type f -name '*.json' -exec echo {}=:{} \; | jo  -p -d '/'

{
   ".": {
      "a.json": {
         "window": "one",
         "top": 10,
         "left": 20
      },
      "bar": {
         "b.json": {
            "window": "two",
            "top": 30,
            "left": 50
         }
      }
   }
}
יש עוד המון מה להגיד על jo אבל אני לא פה בשביל להחליף את התיעוד. אם אהבתם את הכלי ורוצים לדעת עליו יותר שווה להעיף מבט גם בדף התיעוד שלהם בקישור https://github.com/jpmens/jo/blob/master/jo.md.

ToCode
1 419
# שלוש דוגמאות חמודות ליצירת JSON-ים עם jo הפקודה jo יודעת לייצר פלט בפורמט JSON לפי הוראות שאנחנו מעבירים לה בשורת הפקודה. זה נשמע פשוט אבל האמת שבזכות אינסוף האפשרויות שלה היא כלי שמאוד חשוב להכיר. בואו נראה שלוש דוגמאות חמודות לשימוש בפקודה כדי לקבל דברים שבלעדיה אולי היה קשה. ## אבל קודם jo בשלוש מילים אם קרה לכם שרציתם לכתוב קובץ בפורמט JSON והסתבכתם איפה לשים את המרכאות, אז אני בטוח שתדעו להעריך כלי אוטומטי שמטפל בכתיב התקין במקומכם. הפקודה jo בשימוש הבסיסי היא כזה כלי. אני מפעיל אותה משורת הפקודה ונותן לה רשימה של מפתחות וערכים כדי לקבל אובייקט json:
$ jo one=10 two=20 three=30
{"one":10,"two":20,"three":30}
מפתחות שמסתיימים בסימן סוגריים מרובעים יהפכו למערך:
$ jo "a[]=10" "a[]=20" "a[]=30" b=hello
{"a":[10,20,30],"b":"hello"}
אבל הרבה פעמים נשתמש במתג -a כדי ליצור מערך:
$ jo -a blue green white
["blue","green","white"]
ובקצת קסמי bash כדי לשלב מערכים עם אוביקטים:
$ jo name=ynon numbers=$(jo -a 10 20 30)
{"name":"ynon","numbers":[10,20,30]}
אז זה היה פשוט ובאמת רק עד לפה jo נשמע כמו אחלה כלי שעוזר לבנות JSON-ים, אבל כמו שנראה בשלושת הדוגמאות הבאות - הוא יכול להיות הרבה יותר מזה. ## חיבור כל השרתים בקובץ hosts לפי כתובת IP נניח שיש לנו קובץ /etc/hosts עם השורות הבאות:
127.0.0.1 localhost
127.0.0.1 mysite
192.168.0.4 hd
192.168.1.10 router
192.168.1.10 player
ואנחנו רוצים להפוך את זה ל JSON בו כל מפתח הוא כתובת IP והערך הוא מערך של כל השמות של אותה כתובת IP. הכלי הראשון שקופץ לראש הוא כמובן jo. בגלל שהערך צריך להיות מערך אני רוצה להעביר ל jo קלט בו המפתח יהיה כתובת ה IP, אחריה סימן סוגריים מרובעים כדי לציין שזה מערך ואז סימן שווה ואז הערך. אני יכול להשתמש ב awk כדי להמיר את הפורמטים:
$ acat hosts | awk '{print $1"[]="$2}'
127.0.0.1[]=localhost
127.0.0.1[]=mysite
192.168.0.4[]=hd
192.168.1.10[]=router
192.168.1.10[]=player
ובשליחה ל jo נקבל:
$ cat hosts | awk '{print $1"[]="$2}'|jo |jq

{
  "127.0.0.1": [
    "localhost",
    "mysite"
  ],
  "192.168.0.4": [
    "hd"
  ],
  "192.168.1.10": [
    "router",
    "player"
  ]
}
## חיבור כל התהליכים על המכונה לקובץ JSON היררכי עכשיו ננסה משחק דומה עם תהליכים על המכונה. אני יודע ש ps מסוגל להדפיס שורה לכל תהליך, ויודע להדפיס לכל תהליך מהו מזהה התהליך שיצר אותו. זה נראה ככה:
$ ps -e -o ppid,pid,comm|head

 PPID   PID COMM
    0     1 /sbin/launchd
    1    89 /usr/libexec/logd
    1    90 /usr/libexec/UserEventAgent
    1    92 /System/Library/PrivateFrameworks/Uninstall.framework/Resources/uninstalld
    1    93 /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/Support/fseventsd
    1    94 /System/Library/PrivateFrameworks/MediaRemote.framework/Support/mediaremoted
    1    97 /usr/sbin/systemstats
    1    98 /usr/libexec/configd
    1   100 /System/Library/CoreServices/powerd.bundle/powerd
יכול להיות מעניין לראות את הרשימה הזאת בתור אוביקט JSON, כאשר המפתח הוא מזהה תהליך והערך הוא אוביקט שבו כל מפתח הוא מזהה תהליך ילד - שנוצר מתוך התהליך שמעליו בהיררכיה. בדוגמה שהדפסתי זה יהיה האוביקט:
{
  "0": {
    "1": "/sbin/launchd"
  },
  "1": {
    "89": "/usr/libexec/logd",
    "90": "/usr/libexec/UserEventAgent",
    "92": "/System/Library/PrivateFrameworks/Uninstall.framework/Resources/uninstalld",
    "93": "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/Support/fseventsd",
    "94": "/System/Library/PrivateFrameworks/MediaRemote.framework/Support/mediaremoted",
    "97": "/usr/sbin/systemstats",
    "98": "/usr/libexec/configd",
    "100": "/System/Library/CoreServices/powerd.bundle/powerd",
    "101": "/usr/libexec/IOMFB_bics_daemon"
  }
}
איך מייצרים כזה אוביקט אתם שואלים? הפעם אני משתמש ביכולת נוספת של jo שהיא קבלת מפתחות מקוננים. שימו לב לפקודה:
$ jo -d . foo.bar.buz=10

{"foo":{"bar":{"buz":10}}}