Powershell Script Metric

Es kommt ja immer wieder vor, dass man die Laufzeiten eines gesamten oder eben bestimmter Abschnitte innerhalb eines Skriptes messen will. Wenn man z.B. wissen will wie lange es dauert eine Datei zu durchsuchen oder Kopiervorgängen oder etc. In Powershell gibt es ein paar Möglichkeiten, um die Laufzeit Metric zu messen.

Man nimmt zwei Datumsobjekte, jeweils eins für den Start und eins für das Ende der Aktion und berechnet die Differenz zwischen beiden.

#snap the start time
$start=(GET-DATE)
 
#do something
sleep 5
 
#snap the finishing time
$end=(GET-DATE)
 
#calculate the difference
$duration = NEW-TIMESPAN –Start $startEnd $end
 
write-host "It took:$($duration.Seconds) sec"

Oder man nimmt das Stopwatch Objekt von System/Diagnostics

#create a new stop watch and start it immediatly
$stop_watch = [system.diagnostics.stopwatch]::startNew()
 
#do something
sleep 5
 
#look at the watch but continue
write-host "Script runtime: $($stop_watch.Elapsed.Hours):$($stop_watch.Elapsed.Minutes):$($stop_watch.Elapsed.Seconds):$($stop_watch.Elapsed.Milliseconds)"
 
#do something more
sleep 2
 
#stop the watch and look again
$stop_watch.stop()
write-host "Script final runtime: 
$($stop_watch.Elapsed.Hours):$($stop_watch.Elapsed.Minutes):$($stop_watch.Elapsed.Seconds):$($stop_watch.Elapsed.Milliseconds)"
http://www.agile-coding.net/powershell-script-metric/

Datei hochladen mit WinSCP

Neulich musste ich mal wieder eine Datei auf einen SFTP Server hochladen. Und wie es immer so ist, ich konnte mich nur noch teilweise daran errinern wie es geht 🙂

Ich wusse noch das ich WINSCP für den Upload benutzt hatte und wie hübsch einfach die Powershell API ist. WINSCP ist so nett und liefert gleich eine .Net Library mit, die man einfach in jedem Powershell über Add-Type einlinken kann.

Zur Vorbereitung, um die Library nutzen zu können, braucht man natürlich erstmal den Host Name vom Server sowie User und Passwort. Zusätzlich braucht man aber auch den Fingerprint des jeweiligen Server Schlüssels. Hierfür empfehle ich einfach mit der Oberfläche von WinSCP mal eine Verbindung zu dem Server aufzubauen und unter dem Menüpunkt Sessions/Info den Finterprint nachzusehen, der Fingerprint sieht dann ungefähr so aus: 69:32:29:58:c0:21:7d:b3:6d:0f:8a:0d:48:ce:e5:f2

Nun aber zum Script, wie immer bei einem Upload sollte man damit beginnen den Ziel Ordner zu überprüfen, ob man die Daten überhaupt dort hinkopieren kann. Sprich welche Dateien liegen den im Zielordner so herum:

#add the library
Add-Type -Path $p_path_to_winscp
 
#preset the options for the session
$session_opt = New-Object WinSCP.SessionOptions
$session_opt.Protocol = [WinSCP.Protocol]::Sftp
$session_opt.HostName = "Server.irgendwo.de"
$session_opt.UserName = "Username"
$session_opt.Password = "Password"
$session_opt.SshHostKeyFingerprint = "ssh-rsa 2048 " + "69:32:29:58:c0:21:7d:b3:6d:0f:8a:0d:48:ce:e5:f2"
 
#create the session object
$session = New-Object WinSCP.Session	
 
# Connect with the set parameters
$session.Open($session_opt)
 
#query for target directory
$directory = $session.ListDirectory("Mein Zielordner auf dem Server")
 
foreach ($fileInfo in $directory.Files){
 
	#debugging
	Write-Host ("{0} with size {1}, permissions {2} and last modification at {3}" -f $fileInfo.Name, $fileInfo.Length, $fileInfo.FilePermissions, $fileInfo.LastWriteTime)
}

Soweit so gut, und jetzt wollen wir ja noch eine Datei hochladen und das sieht dann in etwa so aus:

 
# Set the transfer options, Binary is always a good choice
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
 
$transferResult = $session.PutFiles("Path to local file", $False, $transferOptions)		
 
# Throw on any error
$transferResult.Check()		
 
# Disconnect, clean up
$session.Dispose()

Und fertig ist der Upload 🙂

http://www.agile-coding.net/datei-hochladen-mit-winscp/

Excel Dateien in XML exportieren mit MDAC

Ich hatte ja schon in diesem Blog über den Export von neueren Excel Datei Formaten mit EPPlus geschrieben. EPPlus kann aber leider nur aktuelle Formate von Excel verarbeiten. Alle älteren Formate funktionieren leider nicht.

Es gibt mehrere Möglichkeiten auch ältere Formate zu verarbeiten. Am einfachsten funktionierte für mich aber die MDAC (Microsoft Data Access Components) Varaiante. Hierfür muss man sicherlich erstmal diese etwas in die Jahre gekommene Framwork installieren. Das charmante an dieser Variante ist, dass man mit einer SQL like Abfrage arbeiten kann z.B.:

„Select * from [$Tabelle1]“

Ich hab es mir einfach gemacht und ein Skript gebaut, was alle Daten, aller Worksheets in eine XML Datei schreibt. Damit ist man im Grunde am flexiblesten, egal was man danach noch alles mit den Daten so machen möchte:

#path to the input excel file
$input="c:\temp\excel.xsl"
 
#path to write the xml output to
$output="c:\temp\ex_ppr.xml"
 
#create the db like driver object
$oleDbConn = New-Object System.Data.OleDb.OleDbConnection
$oleDbCmd = New-Object System.Data.OleDb.OleDbCommand
 
#set the connection string
$oleDbConn.ConnectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=$p_cpf_input;Extended Properties=Excel 12.0;Persist Security Info=False"
 
#open the connection
$oleDbConn.Open()
$oleDbCmd.Connection = $OleDbConn
 
#get the tables/sheet names
$tables = $oleDbConn.GetSchema("Tables")
 
#create the basic xml output nodes
[System.XML.XMLDocument]$xml=New-Object System.XML.XMLDocument
$xml = [xml] "<!--?xml version='1.0' encoding='utf-8'?-->"
[System.XML.XMLElement]$xml_root=$xml.CreateElement("root")
 
 
#init worksheet counter
$ws = 1
 
foreach($table in $tables){
 
	#get the local table name
	$t = $table.TABLE_NAME
 
	#skip all useless tables
	if($t -like "*xlnm*" -or $t -like "*deleted*"){
		continue
	}else{
 
		#tim the ' from the string
		$t = $t.Trim("'")
 
		#reset the db objects
		$oleDbAdapter = New-Object System.Data.OleDb.OleDbDataAdapter
		$dataTable = New-Object System.Data.DataTable
 
		#assemble sql statement
		$oleDbCmd.commandtext = "Select * from [$t]"
		$oleDbAdapter.SelectCommand = $OleDbCmd
 
		#execute the command
		$ret=$oleDbAdapter.Fill($dataTable)
 
		#create new worksheet level
		[System.XML.XMLElement]$xml_ws=$xml.CreateElement("worksheet")
		$t = $t.Trim("$")
		$xml_ws.SetAttribute("name", $t)
 
		#init counter
		$row = 1
 
		#per each result in the dataset
		foreach($data in $dataTable){
 
			#create the row node
			[System.XML.XMLElement]$xml_row=$xml.CreateElement("row")
			$xml_row.SetAttribute("id", $row)
 
			#init column counter
			$col = 1
 
			foreach($cell in $data.ItemArray){
 
				[System.XML.XMLElement]$xml_item=$xml.CreateElement("item")
				$xml_item.SetAttribute("column", $col)
				$xml_item.InnerText = $cell
				$xml_row.appendChild($xml_item)
 
				#count up the column
				$col ++
 
			}
 
			#append to the row
			$xml_ws.appendChild($xml_row)
 
			#count up
			$row ++
		}
		#append the worksheet to the xml
		$xml_root.appendChild($xml_ws)
	}
}
 
#append anything to the root
$xml.appendChild($xml_root)
 
#save the xml file and done
$xml.Save($output)

Das skript ignoriert alle möglichen „sinnlosen“ Worksheets. Wie ich seht, überlese ich alles was im Tabellennamen irgendwas mit „delete“ oder „xlnm“. Probiert mal mit ein paar Excel Dateien, ihr werdet sehen dass da aller Hand an Backups und irgendwelcher Datenmüll in den Excel Dateien gespeichert ist. Komischerweise stecken auch in den Tabllennamen hier und da Anführungszeichen….auch diese entferne ich damit man sauber durch alle Zellen aller (wirklichen) Worksheets durchlaufen kann.

http://www.agile-coding.net/excel-dateien-in-xml-exportieren-mit-mdac/

Excel Dateien in XML exportieren mit EPPlus und Powershell

Neulich musste ich für ein Kundenprojekt eine Excel Datei auslesen. Beim Suchen im Internet fand ich viele Libaries und auch natürlich die Application Com-Object Variante von Microsoft. Da ich aber auf dem Server jetzt kein Excel installieren wollte, fiel die Com-Object Variante aber gleich wieder raus aus der Recherche.

Ich hab mich dann erstmal auf epplus konzentriet und siehe da, es hat einfach nur bestens funktioniert. Im folgenden Powershell Skript, hab ich versucht keine Businesslogik einzubauen, sondern einfach die im Excel enthaltenen Daten in einer XML Datei zu schreiben. Und die Businesslogik dann entsprechend auf das XML anzuwenden:

param(
	#path to the epplus.dll
	$p_epp_plus="C:\Excel\EPPlus.dll",
	#path to the input excel file
	$p_input="C:\Excel\test.xlsm",
	#path to write the xml output to
	$p_output="C:\Excel\test.xml"
)
#isolate the column letter from the address
function get_column($str){
	$str = [regex]::Matches($str, "[a-zA-Z]*")
	$str = $str.groups[0].value
	return $str
}
#isolate the row number from address
function get_row($str){
	$str = [regex]::Matches($str, "[0-9]+")
	$str = $str.groups[0].value
	return $str
}
try{
 
	#adding the libary
	Add-Type -Path $p_epp_plus
 
	#creating the excel object and open the file
	$obj = New-Object OfficeOpenXML.ExcelPackage($p_input)
 
	#creating the xml object
	[System.XML.XMLDocument]$xml=New-Object System.XML.XMLDocument
	$xml = [xml] "<!--?xml version='1.0' encoding='utf-8'?-->"
	[System.XML.XMLElement]$xml_root=$xml.CreateElement("root")
 
	#going thru each of the worksheets
	foreach($ws in $obj.Workbook.Worksheets){
 
		#create an xml node for each worksheet
		[System.XML.XMLElement]$xml_worksheet=$xml.CreateElement("worksheet")
		$xml_worksheet.SetAttribute("index",$ws.index)
		$xml_worksheet.SetAttribute("name",$ws.name)
 
		#going thru each cell of the current worksheet
		foreach($cell in $ws.cells){
 
			#only is a field is not empty
			if($cell.text -ne ""){
				#create an xml node for each cell
				[System.XML.XMLElement]$xml_cell=$xml.CreateElement("cell") 
 
				#getting the address
				$xml_cell.SetAttribute("Address",$cell.Address)
 
				#getting the column
				$column = get_column($cell.Address) 
				$xml_cell.SetAttribute("column", $column)
 
				#getting the row
				$row = get_row($cell.Address)
				$xml_cell.SetAttribute("row", $row)
 
				#content of the cell
				$xml_cell.SetAttribute("text",$cell.text)
 
				#finally adding the row nodes to the worksheet
				$xml_worksheet.appendChild($xml_cell) | out-null
			}
		}
		#finally adding the row nodes to the worksheet
		$xml_root.appendChild($xml_worksheet) | out-null
	}
	#adding everything to the root xml node
	$xml.appendChild($xml_root) | out-null
	#save to given output path and filename
	$xml.Save($p_output)	
}
catch{
	write-host $_.Exception.Message
}

Das Skript liest einfach alle Felder in allen Tabellenblätter (worksheet) und legt alle gefüllten Felder in einem eigenen XML Knoten ab. Zusätzlich wird die Koordinate als Attribute gespeichert, damit man den Inhalt jeweils der gelesenen Postionen auch später noch zuordnen kann.

Der Output sieht im Grunde so aus:

Viel Spaß beim Excel Daten auslesen 🙂

http://www.agile-coding.net/excel-dateien-in-xml-exportieren-mit-epplus-und-powershell/

IMAPX mit Powershell nutzen um Emails abzufragen via IMAP

IMAP ist ein Protokoll, über dem man mit einem Mail Server kommunizieren kann, und beispielsweise Emails abzufragen. Es gibt eine ganze menge IMAP Komponenten im Netz verfügbar. Wegen einem Projekt musste ich mir ein paar davon anschauen und bin schlussendlich bei IMAPX stehen geblieben. Von allen gab es hier die meisten IMAP Funktionen und man kann es, dank der DLL, in eigene Projekte leicht einbinden.

Hier ein Beispiel Powershell Skript zum Abfragen von Emails im Posteingangsordner des Mail Servers:

# set path to libary
$lib = "C:\imapx.dll"
# Import the dll
[Reflection.Assembly]::LoadFile($lib)
# Create a imap object
$imap = New-Object ImapX.ImapClient
# set the fetching mode to retrieve message 
$imap.Behavior.MessageFetchMode = "Full"
# set address of IMAP server
$imap.Host = $server
# set port of server to provide IMAP
$imap.Port = $port
# use encrypted communication
$imap.UseSsl = $true
# try to connect the IMAP server
$imap.Connect()
# setting credantials
$user = "User"
$password = "Password"
# Login with given credentials
$imap.Login($user,$password)
# go and get the message at IMAP server
$messages = $imap.Folders.Inbox.Search("ALL", $imap.Behavior.MessageFetchMode, 1000)
#
foreach($m in $messages){
	write-host 'uid' $m.uid
	write-host 'subject' $m.Subject
	write-host 'from object' $m.from
	write-host 'DisplayName' $m.from.DisplayName
	write-host 'email Address' $m.from.Address
	# get either HTML or text version of the email
	#write-host 'mail body txt' $m.body.text
	write-host 'mail body html' $m.body.html
 }

Die IMAPX Komponente bietet eine Fülle von IMAP Funktionen, z.B. Löschen, Herunterladen, Verschieben, etc.

http://www.agile-coding.net/imapx-mit-powershell-nutzen-um-emails-abzufragen-via-imap/