Showing posts with label functions. Show all posts
Showing posts with label functions. Show all posts

Sunday, September 9, 2007

Converting PowerShell scripts

 

I released this scripts about a week ago, in respond to a discussion in the Windows PowerShell news group. The goal is to read a script content and replace all command-let names to there alias's and vice versa.

The two functions below takes a script file path as the argument and convert it respectively. 

The first function, "Convert-CmdletToAlias", is more *secure* to use as it search for all cmdlet names (which doesn't contain non special chars) and replaces each one it finds with the first corresponding alias (there can be more than one).

The second function, "Convert-AliasToCmdlet", is more *dangerous*. Richard Siddaway reported that there is one major draw back. If you used the "%" sign as an alias for the foreach-object cmdlet, things may go wrong and behave unexpectedly. The reason is that "%" is also the symbol for modulo.

Keith hill suggested to parse the script before the final conversion. Didn't find a way so far so any suggestions/modifications are welcomed.


#### EDIT (11/01/07)  #####
Check out Keith's post on parsing the script for errors prior to executing it
PowerShell QuickTip: Preparsing Scripts to Check for Syntax Errors
#########################


Anyway, the conversion is not written back to the script file and only displayed back to the console. If you decide to use the functions, make sure to create a backup copy of the original scripts and always test the final converted script before using it in production.

 

 

Usage:

Convert-CmdletToAlias <filepath.ps1>
Convert-AliasToCmdlet <filepath.ps1>

 

###############################

 

function Convert-CmdletToAlias{
    param([string]$filepath)
    if(!(test-path $filepath)){
        write-warning "Error: File doesnt exist";
        break;
    }
    $src = [System.IO.File]::ReadAllText($filepath);
    $cmdlets = get-command | where {$_.CommandType -eq "Cmdlet"} | select name
    $cmdlets | foreach {

        $cmd = $_.name;

        if($src -match "\b$cmd\b") {
            $alias = @(get-alias | where {$_.definition -eq $cmd});   
            $src=$src -replace($cmd,$alias[0]);
        }
    }
    $src;
}

 

#####################

 

function Convert-AliasToCmdlet{

    param([string]$filepath)
    if(!(test-path $filepath)) {
        write-warning "Error: File doesnt exist";
        break;
    }
    #cls
    $src = [System.IO.File]::ReadAllText($filepath);
    $aliases = get-alias | select Name,Definition

    $aliases | foreach {

        trap{write-host "ERROR cant process alias: <$alias> -f red -b black"; continue}
        $alias = @($_)[0];
        $regex=[regex]::escape($alias.name);
        # for some reason this is not working "\b" +$regex +"\b"
        if($src -match "\s" +$regex +"\s"){
            $src=$src -replace($regex,$alias.definition);
        }
    }
    $src;       
}

Friday, September 7, 2007

Two PowerShell filters to aid with IP addresses


There are two major differences between functions and filters in PowerShell:

  1. The keyword, function vs filter.
  2. The available current variable inside the function/filter.

Both procedures can be used in pipelines.
When a function is used, the special variable $input is available to process through the input collection. For filters, the special variable $_ contains the current pipeline object.

Filter, as its name suggests will pass on only certain objects down the pipeline.
To demonstrate it, here are two filters to help where IP address are involved.

 

I have a text file were each line contains an IP address.

###### ip.txt #####

10.0.0.138
1.1.1.1111
192.168.2.1
10.0.0.101
10.0.0.102
10.0.0.103

###### EOF #######

 

I want to get the content of this file and operate ONLY on live hosts.
The first filter will validate the IP address and stream down the pipe only valid ones.
The second filter will ping each valid host passed and test its availability,.
The third, ForEach cmdlet,  will run some code on the filtered IPs.

 

filter Validate-IPAddress {
    trap{write-host "$_ <$input>" -f red -b black; continue;}   
    [System.Net.IPAddress]::Parse($_).IPAddressToString;   
}

 

filter Ping-Host{
    begin{
        # instantiate once for all piped objects
        $ping = new-object System.Net.NetworkInformation.Ping;
    }
    process{   
        # ping test
        $stat=$ping.Send($_).status;
        if($stat -ne "Success"){write-warning "$_ is not available <$stat>";}
        else {$_;}
    }   
}

 

# cat = Get-Content
# -f = foreground
# -b = background

cat ip.txt | Validate-IPAddress | Ping-Host | foreach {    
    write-host "$_ is ALIVE, run some code" -f green -b black
}

 

# colored output , just for demonstrating

10.0.0.138 is ALIVE, run some code
An invalid IP address was specified. <1.1.1.1111>
WARNING: 192.168.2.1 is not available <TimedOut>
10.0.0.101 is ALIVE, run some code
WARNING: 10.0.0.102 is not available <TimedOut>
10.0.0.103 is ALIVE, run some code

################################################

 

Here's the filters final version. You can see that only validated IPs and live hosts are processed.

 

filter Validate-IPAddress {
    trap{continue;}   
    [System.Net.IPAddress]::Parse($_).IPAddressToString;
}

filter Ping-Host{
    begin{ $ping = new-object System.Net.NetworkInformation.Ping; }
    process{ if($ping.Send($_).status -eq "Success") {$_;} }   
}

 

cat ip.txt | Validate-IPAddress | Ping-Host | foreach {
    write-host "$_ is ALIVE, run some code" -f green -b black
}

 

# output

10.0.0.138 is ALIVE, run some code
10.0.0.101 is ALIVE, run some code
10.0.0.103 is ALIVE, run some code