Interagire con Windows tramite Ruby

Oggi stavo effettuando alcune ricerche su internet quando sono incappato in un mio messaggio inviato qualche mese fa sulla mailing list di ruby-it in risposta a chi chiedeva aiuto su come poter recuperare l'elenco delle unità montate su un sistema Windows. Ruby ormai offre da tempo un supporto particolarmente maturo all'interazione con il sistema su piattaforme Windows tramite Win32OLE e alle svariate librerie del progetto Win32 Utils. In questo caso, a dimostrazione della flessibilità delle soluzioni offerte da Ruby, lo stesso task può essere risolto in tre modi molto differenti tra loro.

Il primo approccio sfruttando il modulo Windows::Volume delle Win32 Utils consiste nell'utilizzare in maniera diretta le API di Windows e la sua implementazione, anche se può apparire grezza e magari non troppo elegante agli occhi dello sviluppatore Ruby puro, è comunque piuttosto facile:

require 'windows/volume' include Windows::Volume def get_drive_letters buffer = " " * 1024 length = GetLogicalDriveStrings(buffer.length, buffer) buffer[0..length].split(0.chr) end p get_drive_letters # => ["A:\\", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\", "H:\\", "I:\\"]

Utilizzando GetLogicalDriveStrings si ottiene una lista di tutte le unità logiche montate nel sistema, indipendentemente dalla loro natura fisica, il tutto sotto forma di buffer contenente l'elenco di lettere di unità separate dal carattere NULL. Questo ci costringe a istanziare un buffer e a estrarne il contenuto con cui è stato riempito dalla funzione API in una maniera abbastanza lontana dal tipico stile di programmazione in Ruby, ma è pur sempre fattibile e tutto sommato non è poi così brutto a vedersi, c'è di peggio in altri linguaggi.

In Windows esistono poi gli oggetti COM messi a disposizione dalla Scripting Runtime Library e uno di questi, tra l'altro forse tra i più utilizzati, è FileSystemObject. Sinceramente sentire questo nome mi fa venire gli incubi tornare in mente a quando svariati anni fa mi è toccato usare ASP 3.0 che, per la cronaca, non ho mai sopportato. Il fatto di non dover usare le API Win32 salendo quindi di livello ci permette di utilizzare semplicemente Win32OLE:

require 'win32ole' def get_drive_letters drives = [] fso = WIN32OLE.new('Scripting.FileSystemObject') fso.drives.each { |drive| drives << "#{drive.driveletter}:\\" } drives end p get_drive_letters # => ["A:\\", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\", "H:\\", "I:\\"]

L'astrazione messa a disposizione da Win32OLE per le collection restituite da oggetti COM include automaticamente un metodo each che ci permette di iterare in maniera familiare e comoda il risultato dalla proprietà Drives dell'istanza di FSO.

Esiste infine la possibilità di utilizzare l'interfaccia messa a disposizione da WMI (Windows Management Instrumentation) per ottenere le stesse informazioni in maniera ancora più intuitiva con un approccio molto SQL-like nell'interrogazione delle informazioni sui sistemi Windows:

require 'win32ole' def get_drive_letters drives = [] wmi = WIN32OLE.connect('winmgmts://') devices = wmi.ExecQuery('SELECT * FROM Win32_LogicalDisk') devices.each { |drive| drives << "#{drive.DeviceId}\\" } drives end p get_drive_letters # => ["A:\\", "C:\\", "D:\\", "E:\\", "F:\\", "G:\\", "H:\\", "I:\\"]

Interrogando la classe WMI Win32_LogicalDisk si può ottenere rapidamente una nutrita serie di informazioni risparmiandosi un sacco di giri o chiamate, delegando tutto al sistema operativo. Inoltre è possibile scremare già in fase di richiesta e molto facilmente il set di risultati desiderato, del resto come abbiamo visto le query WMI ricordano molto da vicino SQL. Per fare un esempio, cambiando la nostra query in SELECT * FROM Win32_LogicalDisk WHERE FileSystem = 'NTFS' è possibile ottenere l'elenco di tutte le unità il cui filesystem è NTFS. Un altro vantaggio non indifferente è quello di poter richiedere le stesse informazioni a computer remoti semplicemente cambiando la stringa di connessione al provider WMI, per esempio utilizzando una stringa di connessione come winmgmts:{impersonationLevel=impersonate}!\\192.168.1.2\root\cimv2 (in un dominio AD può tornare molto comodo per tenere sotto controllo le proprie installazioni).

E' possibile fare tutto e in molte maniere, ce ne è per tutti i gusti. Il vantaggio di poter interagire con Windows attraverso Ruby anziché con i soliti VBScript e JScript risiede prima di tutto nella sua estrema flessibilità rispetto ai suddetti linguaggi di scripting rivali forniti di serie nel sistema operativo, ma anche nella possibilità di sfruttare nei propri script di automazione tutte le classi standard e di terze parti che gravitano intorno a Ruby è un vantaggio da non sottovalutare nello sviluppo di task amministrativi più complessi. Ok posso percepire anche coloro che ci terrebbero a sottolineare come sia possibile fare le stesse cose con Perl e Python... sì, avete perfettamente ragione, ma qui si pubblicizza Ruby ;-P


Puoi scrivere un commento oppure inviare un trackback dal tuo sito.

2 commenti a “Interagire con Windows tramite Ruby”

  1. Gravatar
    Enrico ha detto:

    Non sono per niente d'accordo sul fatto che ci sia di peggio in altri linguaggi (e del link che segnali).
    Ruby è molto potente ed è ben scritto ma anche il PHP non è male! Ovviamente PHP ha come suo ambiente naturale la programmazione web ed è lì che eccelle mentre lascia molto a desiderare in ambito CLI. Ruby è invece più completo e si presta bene ad entrambi i ruoli (ricordiamo che è nato come linguaggio di scripting per facilitare il lavoro di un sistemista) ma non eccelle in prestazioni.
    Ecco quindi che ogni linguaggio va considerato per quello che è il suo scopo e, come ho già detto, il suo ambiente naturale. D'altronde anch'io potrei portarti un esempio si una pagina PHP e poi confrontarla con un CGI scritto in C... sono linguaggi per ambienti e compiti diversi.

    Detto questo, ti faccio tanti complimenti per il tuo blog che seguo sempre con molto interesse.

    P.S. In riferimento all'articolo... anch'io NON sopporto le ASP3.0!!!!
    Ciao,
    Enrico.

  2. @Enrico: forse sono stato frainteso, con peggio e brutto mi riferivo semplicemente al colpo d'occhio che si ha guardando il codice ma senza particolari considerazioni tecniche, diciamo che era solo un'affermazione molto approssimativa che puntava a giudicare meramente l'"estetica" del codice dal punto di vista di uno sviluppatore Ruby e delle sue abitudini.

    Se dovessi guardare il codice di quella classe PEAR con gli occhi di uno sviluppatore PHP lo troverei comunque ben fatto e abbastanza elegante, ma come valutazione prettamente soggettiva una volta messi fianco a fianco e indipendentemente dal contesto applicativo trovo e ho sempre trovato mediamente più "bello a vedersi" il codice scritto in Ruby piuttosto che quello in PHP pur essendo abituato a entrambi i linguaggi ormai da tanti anni.

    Grazie comunque per i complimenti.

Lascia un commento

Puoi utilizzare i seguenti tag XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>