שמונה דוגמאות ל Ruby One Liners משורת הפקודה ביוניקס
רובי אומנם נחשבת לשפה High Level שעוזרת למתכנתים לכתוב קוד קריא, אבל לרובי יש גם צד אפל במשפחה וזו המורשת שהיא קיבלה מ perl. בדומה ל perl, גם לרובי יש מצב שורת פקודה שבעזרת כמה מתגים בהרצה יאפשר לכם כוחות על יוניקסאיים. הנה 8 דוגמאות להפעלת רובי משורת הפקודה-
1. הדפסת 10 שורות ראשונות מהקלט - בסגנון head
כשמפעילים את רובי משורת הפקודה אנחנו בדרך כלל מעבירים בתור פרמטר שם של קובץ שמכיל תוכנית רובי. אבל אם במקום שם קובץ נעביר את המתג -e
אז רובי ייקח את הדברים הבאים שנכתוב בשורת הפקודה בתור התוכנית שצריך להריץ.
אם נוסיף לזה -n
, רובי ידמיין תוכנית שמורכבת מלולאה שקוראת שורות ובתוך הלולאה נמצא הקוד שאנחנו העברנו לפקודה.
ואם נוסיף לזה -p
אז רובי ידמיין שיש שם גם פקודה הדפסה.
לרובי, כמו לפרל, יש משתנים גלובאליים שאומרים לנו כל מיני דברים לגבי הקלט והמקום שלנו בו. לדוגמה המשתנה הגלובאלי $.
מכיל את מספר שורת הקלט הנוכחית.
אם אני משלב את כל אלה לפקודה אחת משורת הפקודה, נוכל לראות שהשורה הבאה מדפיסה את 10 השורות הראשונות מהקלט שהיא מקבלת ואז עוצרת:
$ ruby -n -e 'print if $. <= 10'
הפקודה יכולה לקבל קלט מקובץ או מ Pipe, כלומר אפשר לכתוב:
$ ls -l | ruby -n -e 'print if $. <= 10'
או:
$ ruby -n -e 'print if $. <= 10' < /etc/passwd
אגב - הפקודה הבאה שקולה פונקציונאלית אבל יעילה יותר על קבצים גדולים:
$ ruby -p -e 'exit if $. > 10'
שימו לב איך -p
גורם לרובי להדפיס באופן אוטומטי כל שורת קלט שהוא נתקל בה, ולכן מספיק לקוד שלי לזהות מתי צריך להפסיק ולצאת מהתוכנית.
2. הדפסת השורה השניה והלאה מהקלט
אומנם head לא יודע "לדלג" על n שורות ראשונות, אבל עם רובי אין לנו שום בעיה לשחק עם הסימנים:
$ ls -l | ruby -p -e 'next if $. < 2'
3. הדפסת 10 שורות אחרונות מהקלט - בסגנון tail
להדפיס את הסוף זה קצת יותר קשה מלהדפיס את ההתחלה, כי אנחנו לא יכולים להדפיס שורה מיד כשאנחנו קוראים אותה. באותו רגע של הקריאה אני עדיין לא יודע כמה שורות סך הכל יהיו בקלט והאם השורה שקיבלתי היא ב 10 שורות האחרונות.
לכן במקום לכתוב קוד שיתייחס לכל שורה בצורה עצמאית יהיה לי יותר קל להתיחס לכל הקלט בתור מערך, ולקרוא לפונקציה last שלו. זה נראה ככה:
$ ls -l | ruby -e 'puts readlines.last(10)'
4. הדפסת כל השורות מהסוף להתחלה
ועם אותו טריק אני יכול להפוך את השורות בקלט לגמרי - כאילו שמנו מראה אחרי השורה האחרונה:
$ cowsay Hello World | ruby -e 'puts readlines.reverse'
|| ||
||----w |
(__)\ )\/\
\ (oo)\_______
\ ^__^
-------------
< Hello World >
_____________
5. סיכום המספרים בעמודה החמישית של הקלט כדי לגלות את הגודל הכולל של הקבצים בתיקיה
הפקודה ls -l
מדפיסה רשימת קבצים מחולקת לעמודות ובעמודה החמישית היא כותבת את גודל הקובץ. לדוגמה בפרויקט רובי יכול להיות שנקבל את הפלט הבא:
$ ls -l
total 72
-rw-r--r-- 1 ynonp staff 2618 Mar 26 09:43 Gemfile
-rw-r--r-- 1 ynonp staff 7368 Mar 22 22:11 Gemfile.lock
-rw-r--r-- 1 ynonp staff 74 Mar 21 17:13 README.md
-rw-r--r-- 1 ynonp staff 227 Mar 21 17:12 Rakefile
drwxr-xr-x 12 ynonp staff 384 Mar 21 22:36 app
drwxr-xr-x 7 ynonp staff 224 Mar 21 17:12 bin
drwxr-xr-x 17 ynonp staff 544 Apr 8 19:09 config
-rw-r--r-- 1 ynonp staff 160 Mar 21 17:12 config.ru
drwxr-xr-x 6 ynonp staff 192 Apr 11 10:03 db
drwxr-xr-x 14 ynonp staff 448 Apr 12 10:55 design
drwxr-xr-x 3 ynonp staff 96 Mar 21 17:15 doc
drwxr-xr-x 3 ynonp staff 96 Mar 21 22:48 engines
drwxr-xr-x 4 ynonp staff 128 Mar 21 17:12 lib
drwxr-xr-x 5 ynonp staff 160 Mar 22 21:54 log
drwxr-xr-x 9 ynonp staff 288 Apr 3 15:49 public
drwxr-xr-x 3 ynonp staff 96 Mar 21 17:12 storage
drwxr-xr-x 14 ynonp staff 448 Mar 26 09:53 test
drwxr-xr-x 9 ynonp staff 288 Mar 26 08:47 tmp
-rw-r--r-- 1 ynonp staff 804 Apr 1 08:46 users.yml
drwxr-xr-x 4 ynonp staff 128 Mar 21 17:12 vendor
-rw-r--r-- 1 ynonp staff 64 Apr 1 08:46 workshop_instances.yml
-rw-r--r-- 1 ynonp staff 171 Apr 1 08:46 workshops.yml
בשביל לסכום את כל המספרים בעמודה החמישית אני יכול להשתמש במחשבון, אבל הרבה יותר קל להיעזר ברובי. הטריק כאן הוא המתג -a
, שגורם לרובי להתנהג כמו awk ולחתוך את הקלט למילים. רשימת המילים נשמרת במשתנה $F
וכל שעלינו לעשות הוא לסכום את המילה החמישית ובסיום התוכנית להדפיס אותה:
$ ls -l | ruby -na -e 'BEGIN { $s = 0 }; END { print $s }; $s += $F[4].to_i'
הפעם עלינו מדרגה ברמת המורכבות בגלל שני הבלוקים BEGIN
ו END
שאולי מוכרים לכם מ awk. בלוק BEGIN
רץ לפני שרובי מתחיל לעבוד ו END
אחרי שהקלט נגמר. בגלל שמשתנים מאותחלים ברובי ל nil, אני צריך להגדיר ולאתחל משתנה גלובאלי שיחזיק את הסכום וכל פעם להגדיל אותו עד ההדפסה בסיום.
דרך קלה לפשט ביטויים כאלה היא לפצל אותם לשניים - החלק הראשון ייקח את העמודה החמישית, והחלק השני יסכום את השורות:
$ ls -l | ruby -na -e 'puts $F[4]' | ruby -e 'puts readlines.map(&:to_i).sum'
6. תדירות הופעת מילים בקלט
הפקודה tally
של רובי יודעת לספור דברים. היא מקבלת מערך ואומרת כמה פעמים כל איבר מופיע במערך זה. בשביל לספור כמה פעמים כל מילה מופיעה בקלט אני יכול לשמור במערך את כל המילים שאני מוצא ואז בבלוק END
להדפיס את ה tally
של הרשימה שבניתי. לדוגמה אם יש לי קובץ בשם text עם התוכן:
some gemstones that are popularly or historically called rubies
such as the black prince ruby in the british imperial state crown
are actually spinels
these were once known as balas rubies
אוכל לדעת כמה פעמים כל מילה הופיעה בקלט עם הפקודה:
$ cat text| ruby -a -n -e 'BEGIN { $words=[] }; $words += $F; END { puts $words.tally }'
{"some"=>1, "gemstones"=>1, "that"=>1, "are"=>2, "popularly"=>1, "or"=>1, "historically"=>1, "called"=>1, "rubies"=>2, "such"=>1, "as"=>2, "the"=>2, "black"=>1, "prince"=>1, "ruby"=>1, "in"=>1, "british"=>1, "imperial"=>1, "state"=>1, "crown"=>1, "actually"=>1, "spinels"=>1, "these"=>1, "were"=>1, "once"=>1, "known"=>1, "balas"=>1}
7. איתור ה Shell הפופולרי ביותר בקובץ `/etc/shells`
הקובץ /etc/shells
על מכונת יוניקס מפרט את כל תוכניות ה shell שמותקנות על המכונה. תוכן לדוגמה של הקובץ יכול להיות:
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.
/bin/bash
/bin/csh
/bin/dash
/usr/local/bin/bash
/bin/sh
/bin/tcsh
/bin/zsh
/usr/local/bin/pwsh
שימו לב שבמקרה שלנו bash מופיע פעמיים כי הוא מותקן משתי תיקיות שונות. אם נדמיין קובץ יותר גדול, אולי נרצה לדעת איזה Shell מותקן הכי הרבה פעמים על המערכת, או לפחות כמה פעמים מותקן כל Shell על המערכת. ורובי עדיין כאן כדי לעזור לנו.
המתג -F
מאפשר לי לקבוע מה יהיה תו ההפרדה בין המילים בקלט. אכוון אותו ל /
ואני מקבל:
$ cat shells | ruby -F/ -na -e 'puts $F[2] if $F.size > 1' | ruby -e 'puts readlines.map(&:chomp).tally'
{"bash"=>1, "csh"=>1, "dash"=>1, "ksh"=>1, "sh"=>1, "tcsh"=>1, "zsh"=>1, "local"=>2}
8. הוספת `-e` לשורת ה shbang של הקובץ
המתג -e
ב sh גורם ל Shell Script לצאת מיד אם פקודה שהוא הריץ נכשלה. לדוגמה הסקריפט הבא יכתוב the end אפילו שהוא לא הצליח ליצור ספריה חדשה בשורש העץ כי לא היו לו הרשאות:
#!/bin/bash
mkdir /foo
echo the end
אבל אם נוסיף לשורה הראשונה את המתג -e
אז הפלט יסתיים בשורת השגיאה של ה mkdir.
אבל מה אם כבר יש לכם ערימה של סקריפטים שאין להם -e
ואתם רוצים להוסיף את המתג לכולם? ניעזר ברובי כמובן. המתג -i
גורם לרובי לשנות את קובץ הקלט עצמו שהוא קיבל, ולכן אם יש לי סקריפט בשם a.sh
אני יכול להפעיל את הפקודה הבאה כדי להוסיף לו אוטומטית את ה -e
בסוף השורה הראשונה:
$ ruby -p -i.bak -e 'sub(/$/, " -e") if $. == 1' a.sh
ואם יש לי ערימה של קבצים כאלה אני יכול לשלב את הפקודה עם find ולהוסיף -e
לכל קבצי ה .sh
בתיקיה:
find . -type f -name '*.sh' -exec ruby -p -i.bak -e 'sub(/$/, " -e") if $. == 1' {} \;