Ruby Mini-Skripte
Hier ist eine Sammlung von Ruby-Miniskripten, die sich bei der täglichen Arbeit so ansammeln.
22.11.2010 :: Context-Menu-Einträge Antivir löschen
Bei Windows 2000 stürzt der Rechner gerne ab, wenn man mit der rechten Maustaste das Context-Menü öffnet. Schuld ist ein Eintrag des Avira Scanners. Mit diesem Script löscht man die Einträge. Blöderweise installieren sich die Einträge bei manchem Update wieder neu.
# 2010-11-22 # Loesche Avira Scanner-Einträge im Context-Menu require 'win32/registry' begin puts 'Loesche *\shellex\ContextMenuHandlers\...' Win32::Registry::HKEY_CLASSES_ROOT.open('*\shellex\ContextMenuHandlers', Win32::Registry::Constants::KEY_ALL_ACCESS) do |reg| reg.delete_key('Shell Extension for Malware scanning', true) end rescue puts '*\shellex\ContextMenuHandlers\... nicht vorhanden.' end begin puts puts 'Loesche *\shellex\ContextMenuHandlers\...' Win32::Registry::HKEY_CLASSES_ROOT.open('Folder\shellex\ContextMenuHandlers', Win32::Registry::Constants::KEY_ALL_ACCESS) do |reg| reg.delete_key('Shell Extension for Malware scanning', true) end rescue puts 'Folder\shellex\ContextMenuHandlers\... nicht vorhanden.' end puts "Tastendruck..." gets
17.08.2007 :: Update-Backups unter W2K löschen
Bei jedem Windows-Update wird im Verzeichniss c:/winnt ein Ordner $NTUninstall<Updatename>$ angelegt, wo die auszutauschenden Dateien vor dem Update gesichert werden. Dies ermöglicht ein Rollback auf den alten Zustand.
Mit der Zeit sammeln sich hier aber jede Menge Verzeichnisse mit einer Menge an Dateien, die das System immer mehr aufblähen. Deshalb sollten die von Zeit zu Zeit gelöscht werden.
Hier ein Skript, was dies automatisch macht. Es werden nur die Inhalte der Verzeichnisse gelöscht, nicht die Verzeichnisse selber. Dies deshalb, weil man so noch gut sehen kann, welche Updates installiert wurden.
Getestet unter Windows 2000 (W2K), muss für andere Windows-Versionen angepasst werden.
# Lösche Windows Backups im im winnt-Verzeichnis # Alle c:\winnt\$NtUninstall* Ordner werden durchlaufen # und die Inhalte gelöscht. Die Ordner bleiben erhalten windir = "c:/winnt" Dir.foreach( windir ) do |f| f_full = File.join( windir, f ) if File.directory?( f_full ) if f =~ /^\$NtUninstall/ delfile = File.join( f_full, "*.*" ) delfile.gsub!( /\//, "\\" ) cmd = sprintf( "del /s /Q %s", delfile ) puts cmd raise unless cmd =~ /del .* c:\\winnt\\\$NtUninstall/ system( cmd ) end end end
24.05.2005 :: G-Code Erzeugung für Planfräsen (CNC, EMC)
Für eine Fräsmaschine, die mit Linux-EMC gesteuert wird, wurde ein kleines Programm gebraucht, welches G-Code für Planfräsen produziert. Im Grunde sollte ein Rechteck mäanderförmig ohne Z-Zustellung plan gefräst werden. Hier ein Programm, welches den G-Code dafür erzeugt.
Übrigens: Auf die neue bdi4-x EMC Version lässt sich der Ruby-Intepreter wunderbar nachinstallieren. Einfach apt-get install ruby eintippen und eine Online-Verbindung haben. Dann sollten die entsprechenden Pakete aus dem Netz heruntergeladen werden. Sobald installiert, kann dieses Programm mit "ruby frplan.rb" gestartet werden, insofern man es unter diesem Namen abgespeichert hat.
Unter Windows hilft der OneClickInstaller für Ruby. Siehe auch http://rubywiki.de/wiki/Erste_Schritte.
Das Ergebnis wird sowohl auf dem Bildschirm ausgegeben wie auch in die Datei test.iso im aktuellen Verzeichnis gespeichert.
# frplan.rb: G-Code Erzeugung für Planfräsen # Gegeben ist die Breite und Länge eines Werkstückes. Dieses # Werkstück soll die Maschine mäanderförmig abfahren, um es # plan zu fräsen. Der Abstand der Mäanderbahnen wird ebenfalls # definiert. # # Winfried Mueller, www.reintechnisch.de # Version = "2005/05/23 - 1" State = "Experimental" def req_is_a?( var, var_class ) raise unless var.is_a? var_class end class Vector def initialize( x, y, z ) req_is_a?( x, Float ) req_is_a?( y, Float ) req_is_a?( z, Float ) @x = x @y = y @z = z end attr_accessor :x, :y, :z end class GCodeGen def initialize @curr_pos = Vector.new( 0.0, 0.0, 0.0 ) @last_line = "" @speed = 0 end attr_reader :curr_pos def speed( speed ) raise unless (speed == 0 || speed == 1) @speed = speed end def speed_as_gcode case @speed when 0 "G00" when 1 "G01" else raise "assert" end end private :speed_as_gcode def go_abs( vect ) x = vect.x - @curr_pos.x y = vect.y - @curr_pos.y z = vect.z - @curr_pos.z @last_line = sprintf( "%s ", speed_as_gcode() ) @moved = false if x != 0 @last_line << sprintf( "X%3.3f ", x ) @curr_pos.x = x @moved = true end if y != 0 @last_line << sprintf( "Y%3.3f ", y ) @curr_pos.y = y @moved = true end if z != 0 @last_line << sprintf( "Z%3.3f", z ) @curr_pos.z = z @moved = true end if !@moved @last_line = "" end end def go_rel( vect ) @last_line = sprintf( "%s ", speed_as_gcode() ) @moved = false if vect.x != 0 @curr_pos.x += vect.x @last_line << sprintf( "X%3.3f ", @curr_pos.x ) @moved = true end if vect.y != 0 @curr_pos.y += vect.y @last_line << sprintf( "Y%3.3f ", @curr_pos.y ) @moved = true end if vect.z != 0 @curr_pos.z += vect.z @last_line << sprintf( "Z%3.3f", @curr_pos.z ) @moved = true end if !@moved @last_line = "" end end def get_last @last_line end def moved? @moved end end def input( prompt, format_str, err_msg ) r = nil loop do print prompt r = gets.chomp if r =~ format_str break end puts "Fehler: #{err_msg}" end r end # -------------------------------------------------------------- # Configuration file_header = <<ENDE % F3000 ENDE file_footer = <<ENDE M30 ENDE file_name = "test.iso" # -------------------------------------------------------------- RXP_FLOAT = /^[0-9]+(\.[0-9]+)?$/ breite = input( "Breite (X): ", RXP_FLOAT, "Format z.B. 20.3" ).to_f laenge = input( "Laenge (Y): ", RXP_FLOAT, "Format z.B. 20.3" ).to_f abstand = input( "Abstand : ", RXP_FLOAT, "Format z.B. 5.0" ).to_f =begin breite = 100.0 laenge = 300.0 abstand = 5.0 =end gen = GCodeGen.new() gen.speed( 1 ) vect1 = Vector.new( breite, 0.0, 0.0 ) vect2 = Vector.new( 0.0, abstand, 0.0 ) vect3 = Vector.new( -breite, 0.0, 0.0 ) vectors = [vect1, vect2, vect3, vect2] f = File.open( file_name, "wb" ) do |f| f.puts file_header f.puts "(frplan generated: X: #{breite} Y: #{laenge} D: #{abstand})" mark_end = false while !mark_end vectors.each do |v| gen.go_rel( v ) if gen.curr_pos.y > laenge mark_end = true break end f.puts gen.get_last if gen.moved? end end f.puts file_footer end File.foreach( file_name ) do |line| print line end puts "Datei #{file_name} im aktuellen Verzeichnis erzeugt." print "Enter Taste fuer weiter..." gets
Hier ein Beispiel für den erzeugten Code:
% F3000 (frplan generated: X: 100.0 Y: 10.0 D: 1.0) G01 X100.000 G01 Y1.000 G01 X0.000 G01 Y2.000 G01 X100.000 G01 Y3.000 G01 X0.000 G01 Y4.000 G01 X100.000 G01 Y5.000 G01 X0.000 G01 Y6.000 G01 X100.000 G01 Y7.000 G01 X0.000 G01 Y8.000 G01 X100.000 G01 Y9.000 G01 X0.000 G01 Y10.000 G01 X100.000 M30
05.04.2005 :: Erzeugen von Test-Dateien
Mitunter brauche ich Test-Dateien, wo ein bestimmtes Muster n-mal wiederholt hintereinander geschrieben ist. Sowas kann man z.B. nutzen, um einen Memorystick zu testen. Man erzeugt eine z.B. 512MB große Datei mit verschiedenen Testpattern, schreibt diese auf den Stick und macht eine md5sum. Ist diese korrekt, ist der Stick in Ordnung.
Dies war übrigens auch der Grund, warum dieses Programm entstand. Ich hatte tatsächlich einen 500MB großen Stick, der defekte Sektoren aufwies, wobei das Betriebssystem jedoch keine Fehler erkannte. Seither teste ich jeden neuen Stick erstmal durch.
Das folgende Programm kann solche Muster-Dateien erzeugen. Man kann die Größe angeben, die die Datei annehmen soll. Das Pattern kann wahlweise als String oder als Hexadezimalfolge angegeben werden.
Dieses Programm zeigt auch schön den Einsatz von optparse. Das Programm läuft erst ab Ruby 1.8.
Mit genpfile --help kann man sich die Benutzung anzeigen lassen.
Die Performance ist ganz ok. Auf einem 450 MHz System erzeugt es unter Windows etwa 15 MB/s Daten.
Aufruf-Beispiele:
Erzeuge eine 512MB große Datei mit 0-Bytes: # genpfile -p 00 -s 512M file.bin Erzeuge eine 10KB große Datei mit dem Muster 0x55AA # genpfile -p 55AA -s 10K file.bin Erzeuge eine 512Byte große Datei mit "Hello World" als Stringpattern #genpfile -t 'Hello World' -s 512 file.bin Zeig mir die Hilfe an: # genpfile --help # genpfile -h
Programm:
# genpfile.rb - Generiere eine Pattern-gefüllte Datei mit # vorgegebener Länge # # # Winfried Mueller, www.reintechnisch.de, (c) 2005, GPL require 'optparse' Version = "1.0.3 - 2007/08/03" options = { :file_size => 1024, :pattern => "\x00", :random => false } # Hexstring (e.g. 'FF00CD0A') to Binary-String def hex_to_bin( hex_value ) hv = hex_value hv.gsub!( / /, "" ) if hv.length % 2 != 0 hv = "0" + hv end bin_value = "" while hv.length > 0 bin_value << hv[0..1].hex hv = hv[2..-1] end bin_value end # Strings as '1K', '10M', '1024' to Integer def size_to_i( size ) mult = 1 rx_KILOBYTE = /K$/i rx_MEGABYTE = /M$/i if size =~ rx_KILOBYTE mult = 1024 elsif size =~ rx_MEGABYTE mult = 1024 * 1024 end (size.scan( /[0-9\.]+/ )[0].to_f * mult).to_i end def fill_random( buf ) 0.upto(buf.length-1) do |i| buf[i] = rand(256).chr end end # Optionparser Setup opts = OptionParser.new do |opts| opts.banner = "Usage: genpfile [options] filename" opts.separator "" opts.on( "-s", "--size SIZE", "File size (e.g. 1024, 1K, 10M)" ) do |size| options[:file_size] = size_to_i( size ) end opts.on( "-p", "--pattern PATTERN", "Write pattern (e.g. '00ABDF51')" ) do |pt| options[:pattern] = hex_to_bin( pt ) end opts.on( "-r", "--random", "Write random data." ) do options[:random] = true end opts.on( "-t", "--text-pattern PATTERN", "Write textpattern (e.g. 'Hello')" ) do |str| options[:pattern] = str end opts.on_tail("-h", "--help", "Show this message" ) do puts opts exit end end begin opts.parse!( ARGV ) file_name = ARGV[0] file_size = options[:file_size] pattern = options[:pattern] raise "No file specified." if file_name == nil File.open( file_name, "wb+" ) do |f| if options[:random] n = 0 srand buf_length = 102400 pattern_buf = "a" * buf_length while n+buf_length < file_size fill_random( pattern_buf ) f.write( pattern_buf ) n+=buf_length end fill_random( pattern_buf ) f.write( pattern_buf[0..(file_size-n-1)] ) else P_MULT = 102400 / pattern.length pattern_buf = pattern * P_MULT pattern_buf_size = pattern_buf.length n = 0 while n + pattern_buf_size < file_size f.write( pattern_buf ) n += pattern_buf_size end f.write( pattern_buf[0..(file_size-n-1)] ) end end rescue (raise "Can't create File #{file_name}") rescue => exc STDERR.puts exc.message STDERR.puts opts.to_s exit 1 end
15.03.2005 :: Linux: Check Medium im CD-Brenner
Unter Linux wird zum CD-Brennen meist cdrecord eingesetzt. Für ein kleines Backupskript brauchte ich die Information, ob ein CD-R oder CD-RW Rohling im Laufwerk liegt. Letzterer muss ja zuvor gelöscht werden, bevor er neu beschrieben wird.
Hierfür ein kleines Skript, welches checkt, ob CD-R oder CD-RW oder gar kein Rohling drin liegt. Hierfür wird `cdrecord -atip` aufgerufen und das Ergebnis geparst.
#!/usr/bin/env ruby # checkcdmedia.rb Version: 2005/03/15 # # Winfried Mueller, www.reintechnisch.de module Config DEVICE = "0,0" CDRECORD = "/usr/bin/cdrecord" end s = `#{Config::CDRECORD} -atip dev=#{Config::DEVICE} 2>null` ret = "Not available" s.each do |line| if line =~ /^\s+Is erasable/ ret = "Is erasable" break end if line =~ /^\s+Is not erasable/ ret = "Is not erasable" break end end puts ret
03.03.2005 :: Automatische Installation Windows Security Updates
Bei Windows 2000 sind es mittlerweile 52 Security-Updates, die man nach einer Neuinstallation einspielen muss. Unbegreiflich, warum Microsoft dafür keine kumulierten Updates rausbringt. Man soll es eben nicht leicht haben, wenn man nicht das automatische Update von denen nutzt.
Aber wir haben ja eine Möglichkeit der Automatisierung. Nahezu jeder Patch kann mit patch.exe /u /z
aufgerufen werden und nervt dann nicht mehr mit interaktiven Abfragen. Jetzt kann man jeden Patch in eine Batchdatei schreiben und dann wird alles automatisch gemacht.
Ich war zu faul, die Batchdatei immer wieder von Hand zu ergänzen, wenn neue Patches hinzu kommen. Also schrieb ich mir ein Ruby-Skript, welches die install.bat automatisch erstellt.
Man legt alle Patches in ein Verzeichnis und benamt die so, dass sie bei Sortierung die richtige Reihenfolge haben. Ich hänge immer das Datum in den Dateinamen vorne an in der Form YYYY-MM-DD-patchname.exe. Ins gleiche Verzeichnis kommt jetzt dieses Ruby-Skript, welches die install.bat erzeugt.
# makebat.rb - Erzeuge Batchfile zur automatisierten # Installation von Microsoft-Security Updates # # Dieses Skript ins Verzeichnis legen, wo Updates liegen. # # Aufruf: ruby makebat.rb # # Erzeugt: install.bat # # Winfried Mueller, www.reintechnisch.de, 2005/03/03 i_files = Dir.entries(".") i_files.delete_if {|f| f !~ /\.exe$/i } i_files.sort File.open( "install.bat", "w+" ) do |of| i_files.each do |f| of.puts f << " /u /z" end end puts "Datei install.bat korrekt erzeugt." puts "Druecke eine Taste fuer weiter..." gets
28.02.2005 :: Kleines hexdump Programm
Dieses Programm stellt eine Inputdatei in Hexadezimalform dar. Sehr hilfreich, um binäre Dateien zu betrachten oder um Sonderzeichen in ASCII-Dateien zu erkennen.
Über START kann man einen Offset angeben, ab dem die Datei ausgegeben werden soll. Default ist 0. BYTES_PER_LINE gibt an, wieviele Bytes pro Zeile ausgegeben werden, sinnvoll ist hier 16 bei einem 80 Zeichen Display.
Changelog:
- 08.04.2005: binmode brauchts für Windows
# hexdump.rb # START = 0x00 BYTES_PER_LINE = 16 pos = 0 lcount = 0 chars = "" ARGF.binmode ARGF.each_byte do |b| if pos < START pos +=1 next end if lcount == 0 printf "%8.8X ", pos end printf "%2.2X ", b if b >= 0x20 && b <= 0x7F chars << b.chr else chars << "." end lcount += 1 pos += 1 if lcount == BYTES_PER_LINE puts chars lcount = 0 chars = "" end end
Aufruf:
ruby hexdump.rb file
Ausgabebeispiel:
00000000 53 54 41 52 54 20 20 20 START 00000008 20 20 20 20 20 20 20 3D = 00000010 20 30 78 30 30 0A 42 59 0x00.BY 00000018 54 45 53 5F 50 45 52 5F TES_PER_ 00000020 4C 49 4E 45 20 3D 20 38 LINE = 8 00000028 0A 0A 70 6F 73 20 3D 20 ..pos = 00000030 30 0A 6C 63 6F 75 6E 74 0.lcount 00000038 20 3D 20 30 0A 63 68 61 = 0.cha 00000040 72 73 20 3D 20 22 22 0A rs = "". 00000048 0A 0A 41 52 47 46 2E 65 ..ARGF.e 00000050 61 63 68 5F 62 79 74 65 ach_byte 00000058 20 64 6F 20 7C 62 7C 0A do |b|. 00000060 20 20 69 66 20 70 6F 73 if pos 00000068 20 3C 20 53 54 41 52 54 < START 00000070 0A 20 20 20 20 70 6F 73 . pos
28.02.2005 :: Klasse eines Objekts checken
s = "Bin ein String" case s when String puts "Ist ein String" when Array puts "Ist ein Array" when IO puts "Ist ein IO" else puts "Unbekannter Typ" end
28.02.2005 :: 512MB 0-Byte Datei erzeugen
Um einen 512MB großen Memorystick zu testen, wollte ich eine 0-Byte und eine 0xFF-Byte Datei erzeugen, die über den gesamten Speicherraum des Sticks geht. Ich hatte die Vermutung, dass bestimmte Bytes auf dem Stick defekt sind. Später konnte ich dann mit einem Hexeditor überprüfen, ob alle Bytes korrekt geschrieben sind. Auch md5sum half dabei.
Das folgende Programm erzeugt also eine etwa 500MB große Testdatei:
f = File.new("e:\\test_00.bin", "w+") x = 0 puts "Erzeuge Datei..." (1024*499).times do f.print "\x00"*1024 if (x+=1) == 1000 print "." x = 0 end end f.close