English · Ελληνικά
Μεταβλητές, συνθήκες, βρόχοι, functions, και ο scheduler — με πραγματικά παραδείγματα: αυτοματισμό Wake-on-LAN, Telegram alerts, και ένα script auto-backup που μπορείς να κάνεις deploy σήμερα.
Κάθε alert Netwatch, κάθε προγραμματισμένο backup, κάθε trigger WoL σε αυτή τη σειρά tutorial είναι ένα script. Το RouterOS έχει μια πλήρη γλώσσα scripting ενσωματωμένη — μεταβλητές, συνθήκες, βρόχους, functions, και έναν scheduler που τρέχει scripts σε οποιοδήποτε χρονικό pattern ορίσεις. Δεν χρειάζεται να ξέρεις Python ή Bash. Απλά χρειάζεται να ξέρεις το syntax, που είναι μοναδικό στο RouterOS αλλά συνεπές και μαθαίνεται σε ένα απόγευμα.
Αυτό το tutorial χτίζει από την αρχή: κανόνες syntax, μεταβλητές, control flow, functions, μετά τρία πλήρη scripts πραγματικού κόσμου που μπορείς να κάνεις deploy αμέσως — ένα Telegram alert όταν οποιοσδήποτε host πέφτει, έναν αυτοματισμό Wake-on-LAN, και ένα auto-backup που σου στέλνει με email ένα αρχείο config κάθε βράδυ.
01
Το scripting του RouterOS έχει το δικό του syntax που δεν μοιάζει με τίποτα άλλο. Μάθε αυτούς τους πέντε κανόνες και τα υπόλοιπα μπαίνουν στη θέση τους.
# ── Κανόνας 1: Τα keywords ξεκινούν με άνω-κάτω τελεία ─────────────────── # Κάθε keyword scripting έχει πρόθεμα ":" # :local :global :if :foreach :while :do :return :log :put :set :log info "this is a log message" :put "this prints to terminal" # ── Κανόνας 2: Οι μεταβλητές δηλώνονται με :local ή :global ────────────── # :local = υπάρχει μόνο σε αυτό το script/function # :global = επιβιώνει σε scripts και εκτελέσεις scheduler :local myName "Hotel-01" :global lastBackup # ── Κανόνας 3: Οι μεταβλητές αναφέρονται με πρόθεμα $ ──────────────────── :local greeting "Hello" :put $greeting # τυπωνει: Hello :put ("prefix-" . $greeting) # το . ενωνει strings → prefix-Hello # ── Κανόνας 4: Τα blocks χρησιμοποιούν { } — ΟΧΙ indentation ───────────── # Το if/else, οι βρόχοι, και τα functions χρησιμοποιούν όλα curly braces για τα σώματά τους :if ($x > 0) do={ :put "positive" } else={ :put "zero or negative" } # ── Κανόνας 5: Τα strings χρησιμοποιούν διπλά εισαγωγικά, οι αριθμοί γυμνοί ── :local name "router-01" # string — διπλά εισαγωγικά :local count 5 # αριθμός — χωρίς εισαγωγικά :local flag true # boolean — χωρίς εισαγωγικά :local ts [/system clock get time] # πιάσε output εντολής με [ ]
02
Το RouterOS έχει έξι τύπους δεδομένων. Το να ξέρεις με ποιον δουλεύεις αποτρέπει το 90% των bugs scripting.
| Τυπος | Παραδειγμα | Σημειωσεις |
|---|---|---|
| str | "hello" | Κείμενο. Ένωσέ το με τον τελεστή .. |
| num | 42 | Ακέραιος. Αριθμητική με + - * /. |
| bool | true / false | Χρησιμοποιείται σε συνθήκες. Επιστρέφεται από συγκρίσεις. |
| ip | 192.168.1.1 | Διεύθυνση IP. Υποστηρίζει λειτουργίες subnet. |
| array | {1;2;3} | Χωρισμένο με άνω-κάτω τελεία. Επανάλαβε με :foreach. |
| nothing | [:toarray ""] | Άδειο / nil. Έλεγξε με [:len $var] = 0. |
# Ένωση strings με . :local hostname [/system identity get name] :local msg ("Alert from: " . $hostname) :put $msg # → Alert from: Hotel-01 # Πιάσε το output εντολής σε μια μεταβλητή χρησιμοποιώντας [ ] :local uptime [/system resource get uptime] :local version [/system resource get version] :put ("Version: " . $version . " Uptime: " . $uptime) # Δήλωση και πρόσβαση array :local servers {192.168.20.50; 192.168.20.51; 192.168.20.52} :put ($servers->0) # → 192.168.20.50 (index από το μηδέν) :put [:len $servers] # → 3 (μήκος του array) # Μετατροπή τύπου :local numStr "42" :local num [:tonum $numStr] :put ($num + 1) # → 43 # Global μεταβλητή — επιβιώνει ανάμεσα σε εκτελέσεις scheduler :global failCount :if (![:isset $failCount]) do={ :set failCount 0 } :set failCount ($failCount + 1) # Χρησιμοποίησε globals προσεκτικά — επιβιώνουν μέχρι το reboot του router.
Αν το string σου περιέχει διπλά εισαγωγικά, κάνε τους escape με ανάστροφη κάθετο: \". Αυτό είναι το πιο κοινό συντακτικό λάθος σε scripts Netwatch. Για παράδειγμα, ένα μήνυμα log μέσα σε ένα down-script πρέπει να γραφτεί ως: :log warning "host \"192.168.20.50\" is DOWN". Το να το κάνεις λάθος κάνει το script να αποτυγχάνει σιωπηλά χωρίς error στο log — ο scheduler απλά παραλείπει την εκτέλεση.
03
Το :if ελέγχει μια συνθήκη. Το :foreach κάνει iterate ένα array. Το :while κάνει βρόχο μέχρι μια συνθήκη να γίνει false. Αυτά τα τρία καλύπτουν κάθε pattern control flow που θα χρειαστείς.
# Βασικό if/else :local x 10 :if ($x > 5) do={ :log info "x is greater than 5" } else={ :log info "x is 5 or less" } # Τελεστές σύγκρισης # = ίσο != όχι ίσο # > μεγαλύτερο απο < μικρότερο απο # >= μεγ/ίσο <= μικρ/ίσο # && λογικό AND || λογικό OR ! NOT # Έλεγξε αν υπάρχει DHCP lease για ένα hostname :local leaseCount [/ip dhcp-server lease count-numbers \ where host-name="fileserver"] :if ($leaseCount > 0) do={ :log info "fileserver has a DHCP lease" } else={ :log warning "fileserver not found in DHCP leases" } # Ένθετη συνθήκη με AND :local cpu [/system resource get cpu-load] :local mem [/system resource get free-memory] :if ($cpu > 80 && $mem < 10000000) do={ :log warning "High CPU and low memory simultaneously" }
# Επανάλαβε ένα στατικό array :local vlans {10; 20; 30; 40} :foreach vlan in=$vlans do={ :put ("Processing VLAN: " . $vlan) } # Επανάλαβε πάνω σε αντικείμενα RouterOS — το find επιστρέφει ένα array εσωτερικών IDs :foreach leaseId in=[/ip dhcp-server lease find \ where status=bound] do={ :local ip [/ip dhcp-server lease get $leaseId address] :local host [/ip dhcp-server lease get $leaseId host-name] :put ($host . " → " . $ip) } # Τυπώνει κάθε bound lease DHCP ως "hostname → IP" # Μέτρα επαναλήψεις με μία μεταβλητή index :local count 0 :foreach i in=[/interface find where running=yes] do={ :set count ($count + 1) } :put ("Running interfaces: " . $count)
# Περίμενε έναν host να ανέβει μετά από WoL (βρόχος ping με timeout) :local target "192.168.20.50" :local attempts 0 :local maxAttempts 12 :local isUp false :while ($attempts < $maxAttempts && !$isUp) do={ :delay 5s :local result [/ping address=$target count=1 as-value] :if ($result->"received" > 0) do={ :set isUp true :log info ("Host " . $target . " is UP after WoL") } :set attempts ($attempts + 1) } :if (!$isUp) do={ :log warning ("Host " . $target . " did not respond after 60s") } # Κάνει βρόχο μέχρι 12 φορές, 5s η καθεμία = 60s μέγιστη αναμονή. # Βγαίνει νωρίς αν ο host απαντήσει. Κάνει log warning αν λήξει το timeout.
04
Τα functions σου επιτρέπουν να γράψεις ένα block λογικής μία φορά και να το καλέσεις από οπουδήποτε — έναν scheduler, ένα script Netwatch, ή άλλο function.
Τα functions του RouterOS δηλώνονται σαν global μεταβλητές που κρατούν ένα script block. Κάλεσέ τα με [$funcName param1=val1]. Μπορούν να δέχονται παραμέτρους και να επιστρέφουν τιμές.
# Δήλωσε ένα function σαν global μεταβλητή :global sendTelegram :set sendTelegram do={ :local message $1 :local token "YOUR_BOT_TOKEN" :local chatId "YOUR_CHAT_ID" /tool fetch url=("https://api.telegram.org/bot" . $token . \ "/sendMessage?chat_id=" . $chatId . \ "&text=" . $message) \ keep-result=no } # Κάλεσε το function — το $1 είναι το πρώτο θετικό argument [$sendTelegram "Test alert from Hotel-01"] # Function με ονομαστικές παραμέτρους :global wakeAndVerify :set wakeAndVerify do={ :local iface $iface :local mac $mac :local target $target /tool wol interface=$iface mac=$mac :delay 45s :local result [/ping address=$target count=3 as-value] :return ($result->"received" > 0) } # Κάλεσε με ονομαστικές παραμέτρους :local success [$wakeAndVerify \ iface=vlan20 mac="AA:BB:CC:DD:EE:FF" target="192.168.20.50"] :if ($success) do={ :log info "Server woke successfully" } else={ :log warning "Server did not respond after WoL" }
Τα global functions που ορίζονται με :global funcName; :set funcName do={...} επιβιώνουν στη μνήμη μόνο μέχρι να κάνει reboot ο router. Όταν ο scheduler τρέχει ένα script που καλεί ένα global function, πρέπει είτε να δηλώσει το function inline, ή το function πρέπει να ρυθμιστεί από ένα startup script που τρέχει πριν από οποιεσδήποτε καταχωρήσεις scheduler. Το ασφαλέστερο pattern για production: βάλε τη δήλωση function στην κορυφή κάθε script που το χρησιμοποιεί. Αυτό είναι πολυλογότερο αλλά αξιόπιστο.
05
Ο scheduler είναι αυτό που κάνει τα scripts λειτουργικά — τα τρέχει σε ένα χρονικό pattern, στο startup, ή σε συγκεκριμένη ημερομηνία και ώρα.
# Τρέξε κάθε μέρα στις 02:00 [admin@MikroTik] > /system scheduler add \ name="daily-backup" \ start-time=02:00:00 \ interval=1d \ on-event="/system script run auto-backup" \ comment="Run auto-backup script nightly" # Τρέξε κάθε 30 λεπτά [admin@MikroTik] > /system scheduler add \ name="health-check" \ interval=00:30:00 \ on-event="/system script run health-check" \ comment="30-minute health check" # Τρέξε στο startup (interval=0, χωρίς start-time) [admin@MikroTik] > /system scheduler add \ name="startup-init" \ on-event="/system script run startup-init" \ start-time=startup \ comment="Initialise globals on boot" # Τρέξε inline script απευθείας (δεν χρειάζεται ξεχωριστό αντικείμενο script) [admin@MikroTik] > /system scheduler add \ name="log-uptime" \ interval=1h \ on-event={:log info ("Uptime: " . [/system resource get uptime])}
/system script run name) ή να κάνεις paste το σώμα του script απευθείας στο On Event.06
Ένα επαναχρησιμοποιήσιμο function alert που κάθε άλλο script μπορεί να καλέσει. Ένα function, ένα μήνυμα Telegram, δουλεύει από Netwatch, scheduler, ή οποιοδήποτε script.
# Όνομα script: telegram-alert # Βάλ' το σε System › Scripts, κάλεσέ το από down-scripts Netwatch ή scheduler. # Απαιτεί: bot token και chat ID Telegram. # Πάρ' τα δωρεάν στο: https://t.me/BotFather :local botToken "1234567890:ABCDefGhIJKlmNoPQRsTUVwxyZ" :local chatId "-1001234567890" :local hostname [/system identity get name] :local uptime [/system resource get uptime] # Χτίσε το μήνυμα — κάνε encode τα κενά ως + για URL :local msg ("🔴 ALERT — " . $hostname . \ "%0AHost: 192.168.20.50 is DOWN" . \ "%0AUptime: " . $uptime . \ "%0ATime: " . [/system clock get time] . \ " " . [/system clock get date]) # Στείλε μέσω Telegram Bot API :do { /tool fetch \ url=("https://api.telegram.org/bot" . $botToken . \ "/sendMessage?chat_id=" . $chatId . \ "&text=" . $msg) \ keep-result=no \ http-method=get :log info "Telegram alert sent" } on-error={ :log warning "Telegram alert FAILED — check internet connectivity" } # :do { } on-error={ } = pattern try/catch. # Αν το /tool fetch αποτύχει (χωρίς internet), κάνει log warning αντί να κρασάρει. # %0A = newline URL-encoded σε μηνύματα Telegram.
07
Ξύπνα το NAS στις 02:00, επιβεβαίωσε ότι ανέβηκε, στείλε μια επιβεβαίωση Telegram. Τρία εργαλεία από αυτή τη σειρά δουλεύουν μαζί σε ένα script.
# Όνομα script: wake-nas-verify # Πρόγραμμα: καθημερινά στις 01:55 (5 λεπτά πριν το job backup) # Υποθέτει: NAS στο 192.168.20.50, MAC AA:BB:CC:DD:EE:FF :local nasIP "192.168.20.50" :local nasMac "AA:BB:CC:DD:EE:FF" :local nasIface "vlan20" :local botToken "YOUR_BOT_TOKEN" :local chatId "YOUR_CHAT_ID" :local hostname [/system identity get name] # Βήμα 1: Έλεγξε αν το NAS είναι ήδη πάνω :local alreadyUp [/ping address=$nasIP count=1 as-value] :if ($alreadyUp->"received" > 0) do={ :log info "NAS already online — skipping WoL" :error "NAS already up" # το :error βγαίνει από το script καθαρά } # Βήμα 2: Στειλε το magic packet :log info ("Sending WoL to " . $nasMac . " via " . $nasIface) /tool wol interface=$nasIface mac=$nasMac # Βήμα 3: Περίμενε και επαλήθευσε (μέχρι 60 δευτερόλεπτα) :local attempts 0 :local isUp false :while ($attempts < 12 && !$isUp) do={ :delay 5s :local ping [/ping address=$nasIP count=1 as-value] :if ($ping->"received" > 0) do={ :set isUp true } :set attempts ($attempts + 1) } # Βήμα 4: Ανέφερε το αποτέλεσμα μέσω Telegram :local status :if ($isUp) do={ :set status "✅ NAS is UP" :log info ("WoL success: NAS responded after " . \ ($attempts * 5) . "s") } else={ :set status "❌ NAS did NOT respond after 60s" :log warning "WoL failed: NAS unreachable" } :local msg ($status . "%0AHost: " . $nasIP . \ "%0ARouter: " . $hostname) :do { /tool fetch url=("https://api.telegram.org/bot" . \ $botToken . "/sendMessage?chat_id=" . \ $chatId . "&text=" . $msg) keep-result=no } on-error={ :log warning "Telegram send failed" }
08
Κάθε βράδυ στις 02:00, κάνε export το config, επισύναψέ το σε ένα email, και στείλ' το εκτός site. Αν ο router γίνει ποτέ brick ή κλαπεί, έχεις το χθεσινό config στα εισερχόμενά σου.
# Πρώτα: ρύθμισε ρυθμίσεις SMTP (setup μία φορά) [admin@MikroTik] > /tool e-mail set \ server=smtp.gmail.com \ port=587 \ start-tls=yes \ from=alerts.example@gmail.com \ user=alerts.example@gmail.com \ password="YOUR_APP_PASSWORD" # Χρησιμοποίησε ένα Gmail App Password (όχι το κύριο password σου). # Δημιούργησέ το στο: Google Account → Security → App passwords # Όνομα script: auto-backup # Πρόγραμμα: καθημερινά στις 02:00 :local hostname [/system identity get name] :local date [/system clock get date] :local dateStr [:pick $date 7 11] # ετος :local dateStr ($dateStr . "-" . [:pick $date 0 3]) # μηνας :local dateStr ($dateStr . "-" . [:pick $date 4 6]) # ημερα :local filename ($hostname . "-" . $dateStr . ".rsc") # Βήμα 1: Κάνε export το config σε αρχείο /export file=$hostname # Δημιουργεί το /hostname.rsc στο flash του router # Βήμα 2: Δημιούργησε επίσης binary backup /system backup save name=$hostname # Δημιουργεί το /hostname.backup στο flash του router # Βήμα 3: Στειλε το export .rsc σαν επισυναψη email :do { /tool e-mail send \ to="you@example.com" \ subject=("RouterOS Backup: " . $hostname . " " . $date) \ body=("Automated backup from " . $hostname . \ "\nDate: " . $date . \ "\nUptime: " . [/system resource get uptime] . \ "\nVersion: " . [/system resource get version]) \ file=($hostname . ".rsc") :log info ("Backup email sent for " . $hostname) } on-error={ :log warning ("Backup email FAILED for " . $hostname) } # Βήμα 4: Καθάρισε παλιά αρχεία backup (κράτα τα τελευταία 3) :local files [/file find where name~($hostname . ".rsc")] :if ([:len $files] > 3) do={ /file remove ($files->0) :log info "Removed oldest backup file" }
Τύλιγε πάντα εξωτερικές κλήσεις σε :do {} on-error={}. Κάθε script που καλεί /tool fetch ή /tool e-mail send θα αποτύχει σιωπηλά αν το internet είναι κάτω. Χωρίς try/catch, το script κρασάρει στη μέση της εκτέλεσης και τα επόμενα βήματα δεν τρέχουν ποτέ. Τύλιξε κάθε κλήση δικτύου και κάνε log ένα warning σε αποτυχία — τότε το υπόλοιπο script συνεχίζει.
Δοκίμασε scripts στο terminal πριν τα προγραμματίσεις. Κάνε paste το σώμα του script απευθείας στο prompt CLI και τρέξ' το διαδραστικά. Παρακολούθα το log με /log print follow σε δεύτερο terminal session. Μόλις δουλέψει σωστά, μετακίνησέ το σε System › Scripts και προγραμμάτισέ το. Μην προγραμματίσεις ποτέ ένα μη δοκιμασμένο script σε production router.
Χρησιμοποίησε το :log γενναιόδωρα κατά την ανάπτυξη, με φειδώ σε production. Κάθε κλήση :log info γράφει στο volatile log buffer του router. Ένα script που τρέχει κάθε 5 λεπτά και κάνει log 10 γραμμές θα γεμίσει το log buffer 1000-γραμμών και θα σπρώξει έξω χρήσιμες καταχωρήσεις. Σε production, κάνε log μόνο αλλαγές state και errors — όχι μηνύματα ρουτίνας επιβεβαίωσης.
Βασικα Συμπερασματα
:local ip [/ip address get 0 address] αποθηκεύει το αποτέλεσμα οποιασδήποτε εντολής RouterOS σε μια μεταβλητή. Έτσι αλληλεπιδρούν τα scripts με το τρέχον config του router.:do {} on-error={} γύρω από κάθε εξωτερική κλήση. Το /tool fetch, το /tool e-mail send, και το /ping μπορούν όλα να αποτύχουν. Χωρίς error handling, ένα script κρασάρει σιωπηλά στη μέση της εκτέλεσης και τα επόμενα βήματα παραλείπονται χωρίς καμία ένδειξη τι συνέβη.start-time=startup του scheduler αντικαθιστά τα autorun scripts. Χρησιμοποίησέ το για να αρχικοποιήσεις globals, να ξεκινήσεις containers, και να επαληθεύσεις καταστάσεις interface στο boot. Είναι πιο αξιόπιστο από την ξεπερασμένη προσέγγιση autorun.Χτίζω και συντηρώ αυτοματοποιημένο monitoring για δίκτυα φιλοξενίας — Telegram alerts τη στιγμή που κάτι πάει στραβά, νυχτερινά backups config που πραγματικά τρέχουν, και προγραμματισμένα scripts συντήρησης αντί να θυμάται κάποιος να το κάνει χειροκίνητα.
Κλεισε μια Discovery Call →