Variables (PowerShell): Difference between revisions
m (→Datetime: Minor rewording) |
m (→Array Types: Update ArrayList create example) |
||
Line 381: | Line 381: | ||
To force the creation of a .NET array from a PowerShell CmdLet, create one in a fashion such as this... | To force the creation of a .NET array from a PowerShell CmdLet, create one in a fashion such as this... | ||
<source lang="powershell"> | <source lang="powershell"> | ||
$a = New-Object System.Collections.ArrayList(,(Get-Content test.txt)) | $a = New-Object System.Collections.ArrayList # Empty array | ||
$a = New-Object System.Collections.ArrayList(,(Get-Content test.txt)) # Populated with contents of test.txt | |||
</source> | </source> | ||
Revision as of 09:17, 16 May 2012
All variable names are prefixed with a $
, and are case insensitive (though there's no reason to not use CamelBack notation if that floats your boat).
Apart from a few reserved words and characters there's little restriction on what can be used, though note that this flexibility can cause the occasional issue, whereby PowerShell gets confused as to where a variable name finishes. Variable names can be enclosed in { }
in order to delimit them, eg ${varname}
.
PowerShell is all about manipulating objects, and all variables are objects. When you assign a value to a new variable, the type of object that it is (string, integer, etc) is automatically defined. Which is normally useful as you don't have to worry about what type of object/variable you want to create, PowerShell will work it out for you. However sometimes you need to force a variable to contain a specific data type to avoid errors or other problems down the line. Using a prefix of [type]
achieves this...
[string]$result = $PingResult.Status
Data types
Notation | Data Type |
---|---|
[bool] |
True / false |
[single] |
Single-precision 32-bit floating point number |
[double] |
Double-precision 64-bit floating point number |
[byte] |
8-bit unsigned character |
[int] |
32-bit integer |
[long] |
64-bit integer |
[decimal] |
128-bit decimal |
[char] |
Single character |
[string] |
String of characters |
[datetime] |
Date and time |
[timespan] |
Time |
[xml] |
XML object |
[array] |
Array |
[wmi] |
Windows Management Instrumentation (WMI) instance or collection |
[wmiclass] |
WMI class |
[adsi] |
Active Directory Services object |
[Boolean] |
True or False value |
Variable Information
As PowerShell object (variables) tend to be black boxes that can contain anything or nothing, its often necessary to understand more about one. All objects contain Properties and Methods.
- Properties
- Are containers for data
- Methods
- Are in-built functions that allow an object to be manipulated.
Variable Type
To see the object type of a variable...
PS E:\> $var.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Variable Properties
Simple objects such as strings will contain only one property but complicated can contain many more, and even further objects. If you pipe an object through Format-List
you get a fuller picture...
PS E:\> Get-WMIObject Win32_BIOS
SMBIOSBIOSVersion : 786F3 v01.16
Manufacturer : Hewlett-Packard
Name : Default System BIOS
SerialNumber : CPC123456J
Version : HPQOEM - 20090219
PS E:\> Get-WMIObject Win32_BIOS | Format-List *
Status : OK
Name : Default System BIOS
Caption : Default System BIOS
SMBIOSPresent : True
....TRUNCATED TO SAVE SPACE !!....
SMBIOSBIOSVersion : 786F3 v01.16
SMBIOSMajorVersion : 2
SMBIOSMinorVersion : 6
SoftwareElementID : Default System BIOS
SoftwareElementState : 3
TargetOperatingSystem : 0
Version : HPQOEM - 20090219
Scope : System.Management.ManagementScope
Path : \\LAPTOP1\root\cimv2:Win32_BIOS.Name="Default System BIOS",SoftwareElementID="Default System BI
OS",SoftwareElementState=3,TargetOperatingSystem=0,Version="HPQOEM - 20090219"
Options : System.Management.ObjectGetOptions
ClassPath : \\LAPTOP1\root\cimv2:Win32_BIOS
Properties : {BiosCharacteristics, BIOSVersion, BuildNumber, Caption...}
SystemProperties : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY...}
Qualifiers : {dynamic, Locale, provider, UUID}
Site :
Container :
Variable Properties and Methods
PS E:\ > $string | get-Member
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Clone Method System.Object Clone()
CompareTo Method int CompareTo(System.Object value), int CompareTo(string strB)
Contains Method bool Contains(string value)
CopyTo Method System.Void CopyTo(int sourceIndex, char[] destination, int destinationIndex,...
EndsWith Method bool EndsWith(string value), bool EndsWith(string value, System.StringCompari...
Equals Method bool Equals(System.Object obj), bool Equals(string value), bool Equals(string...
GetEnumerator Method System.CharEnumerator GetEnumerator()
....TRUNCATED TO SAVE SPACE !!....
ToString Method string ToString(), string ToString(System.IFormatProvider provider)
ToUpper Method string ToUpper(), string ToUpper(System.Globalization.CultureInfo culture)
ToUpperInvariant Method string ToUpperInvariant()
Trim Method string Trim(Params char[] trimChars), string Trim()
TrimEnd Method string TrimEnd(Params char[] trimChars)
TrimStart Method string TrimStart(Params char[] trimChars)
Chars ParameterizedProperty char Chars(int index) {get;}
Length Property System.Int32 Length {get;}
It's not uncommon for objects provided by API's to provide methods that are in fact objects themselves, you can end up with a lot of information being available once you're capable of drilling into them.
NULL
Checking whether a variable is NULL or not can be problematic. In that the easiest test doesn't always work.
Basic test for whether a variable exists or not, works in most cases...
if (!$var) {
"Variable is null"
}
If you expect your variable to be NULL or contain a positive number...
if ($var -gt 0) {
"Variable is NOT null"
}
Last resort test...
if ($var.gettype().ToString() -eq "System.DBNull") {
"Variable is null"
}
Scope
Variables only fully exist within the scope of the script or function within which they are defined. Within functions local copies of a variable are available, but manipulating them has no effect on the real/master variable in the main scope, to get around this you can use script
to enforce using the variable that's in the main script's scope...
function Local-Add($text) {
$List += $text
}
function Global-Add($text) {
$script:List += $text
}
$List = @()
$List += "Text message ONE"
$List.Length # List contains one entry
Local-Add "Text message TWO"
$List.Length # List still contains one entry
Global-Add "Text message THREE"
$List.Length # List now contains two entries
Similarly, you can create a variable within a function that has global scope...
function Create-Var {
if (!$TestVar) {
$script:TestVar = "stuff"
Write-Host 'Created $TestVar'
} else {
Write-Host '$TestVar already exists'
}
}
Create-Var
Create-Var
Write-Host '$TestVar contains [' $TestVar ']'
...which when run produces the following output...
Created $TestVar $TestVar already exists $TestVar contains [ stuff ]
You can achieve the same result by using the Set-Variable
CmdLet with the -Scope
parameter instead of script
, for example...
function Create-Var {
if (!$TestVar) {
#$script:TestVar = "stuff"
Set-Variable -Name TestVar -Value "stuff" -Scope 1
Write-Host 'Created $TestVar'
} else {
Write-Host '$TestVar already exists'
}
}
Create-Var
Create-Var
Write-Host '$TestVar contains [' $TestVar ']'
Strings
Basic manipulation tasks can be carried out by using the string object's methods, eg "string".PadRight(10)
, see Get-Member -InputObject "Text"
for full details.
Concatenation +
$strAB = $strA + $strB
Interpolation
Interpolation allows variables to be embedded into a string and to be resolved into their actual values. This works between double quotes, but not between single quotes...
PS E:\> $sub = "replaced"
PS E:\> Write-Output "Variable has been $sub"
Variable has been replaced
PS E:\> Write-Output 'Variable has been $sub'
Variable has been $sub
Search
To search for specific text in a string...
if (Select-String -InputObject $text -Pattern "StringToFind" -Quiet)
{
# StringToFind found in $text
}
Match (basic)
To do a basic search/match...
if ($res.Contains("Success")) {
# String did contain
} else {
# Didn't
}
...which is much preferable to...
if ($res.CompareTo("Success")) {
# Didn't match (CompareTo returns 1 if comparison fails !)
} else {
# Did match
}
Match (extract)
To extract text that matches a regex...
$res = [regex]::matches($line, "\d{4}-[A-Za-z]{3}-Week\d{1}.log")
if (-not $res.Count)
{
# No matches found
} else {
$res1 = $res.Item(1).Value # 1st match to regex
}
See Regular Expressions for further info on regex stuff.
Replace
Basic find and replace can be done with a string's Replace method, eg to replace "\" with "\\" in the $query variable...
$query = $query.Replace("\", "\\")
For proper regular expressions support, use the following syntax
$query = [regex]::Replace($query, "search", "replace")
Strip Whitespace
$string = $string.TrimEnd()
Escape Characters
Text | Description |
---|---|
`0 |
Null |
`a |
Bell/system beep |
`b |
Backspace |
`f |
Form feed |
`n |
New line |
`r |
Carriage return |
`t |
Tab (horizontal) |
`v |
Vertical tab |
`' |
' |
`" |
" |
Arrays
$array = @() # Create blank array
$array += 34 # Add value to end of array
To create an array (table) with column headings, initialise an array, then create a row variable with the column headings and add this row to the array. This is convenient when building a table of data within a loop eg
$table = @()
foreach ($instance in $set) {
$row = "" | Select Heading1, Heading2, Heading3
$row.Heading1 = "something"
$row.Heading2 = "like"
$row.Heading3 = "this"
$table += $row
}
Add rows to an array
> $array = @()
> $row = "" | Select h1,h2,h3
> $row.h1 = "esx1"
> $row.h2 = "HBA1"
> $row.h3 = "LUN1"
> $array = $array + $row
> $row = "" | Select h1,h2,h3
> $row.h1 = "esx2"
> $row.h2 = "HBA1"
> $row.h3 = "LUN2"
> $array = $array + $row
> $array
h1 h2 h3
-- -- --
esx1 HBA1 LUN1
esx2 HBA1 LUN2
Select row from array
Using above array as example...
> if (($array |?{$_.h1 -eq "esx2"})) {"true"} else {"false"}
true
> if (($array |?{$_.h1 -eq "esx3"})) {"true"} else {"false"}
false
> $array |?{$_.h1 -eq "esx2"}
h1 h2 h3
-- -- --
esx2 HBA1 LUN2
> $array |?{$_.h1 -eq "esx2"} | Select -ExpandProperty h2
HBA1
Array Types
.NET Array Lists are far more flexible than PowerShell arrays. With ArrayLists you can easily Add and Remove members and generally enjoy much more flexibility when it comes to manipulating its contents. Despite showing a full range of available methods (when piped through a Get-Member
), PS arrays generally don't have many available methods. To confirm the type that you have...
> $ArrayList.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True ArrayList System.Object
> $PSArray.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
To force the creation of a .NET array from a PowerShell CmdLet, create one in a fashion such as this...
$a = New-Object System.Collections.ArrayList # Empty array
$a = New-Object System.Collections.ArrayList(,(Get-Content test.txt)) # Populated with contents of test.txt
Hashtables
$hash = @{} # Create blank array
$hash["Name"] = "Value" # Add value to end of array
$hash.GetEnumerator() | Sort-Object -Property Name # Sort hashtable
Datetime
The object structure: http://msdn.microsoft.com/en-us/library/system.datetime.aspx
Improper (US) Formatting
Be aware that if you live somewhere dates are normally formatted properly (eg not the USA), then PowerShell (or probably the underlying DateTime object type as this sort of problem seems to rear its head at unexpected moments when working on Windows) has a nasty habit of returning a string formatted with day and month swapped around when coming from a script.
If you do a Get-Date
it all looks fine, but then you output a DateTime object in a script to some text and its wrong. Add the .ToString()
method to the end at it'll sort itself, though quite why when Powershell is converting the object into a string anyway in order to display, should the object need to be explicitly told to fix the issue, seems a bit superfluous. I obviously don't understand what the underlying bug is!
If your dates are getting mixed up, it may not be your mistake, and it may be that you've fallen fowl of the problem as well.
Formatting
To control how a DateTime is displayed you can pass it through Get-Date
with the -uFormat
option...
Get-Date $datetime -uFormat "%R hrs, %a %d %b %Y"
Useful formatting examples...
uFormat Specifier | Example |
---|---|
%R hrs, %a %d %b %Y |
07:25 hrs, Fri 24 Dec 2010
|
%Y-%m-%d |
2010-12-24
|
For the full list of formatting options see http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo.aspx
Or you can use the ToString
method provided by the object, to convert the date. If you use a generic specifier you should get a local format output (though see note above about Improper (US) Formatting if things aren't behaving expected)...
$datetime.ToString("s")
Useful formatting examples...
ToString Method Specifier | UK-Centric Example Output |
---|---|
<blank> |
17/02/2011 13:07:33
|
d |
17/02/2011
|
D |
17 February 2011
|
yyyy-MM-dd HH:mm:ss |
2011-02-17 13:07:33
|
HH:mm:ss.fff |
13:07:33.423
|
For the full list of formatting options see http://technet.microsoft.com/en-us/library/ee692801.aspx, and even more detail at http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo.aspx
Converters
function ConvertLocalToUnix([datetime]$datetime)
{
($datetime.ToUniversalTime() - ([datetime]'1/1/1970 00:00:00')).TotalSeconds
}
function ConvertUnixtoLocal($sincepoch)
{
[timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($sincepoch))
}
Environment
Environmental variables can be accessed via $env
$env:userprofile # User profile (eg C:\Users\joeblogs)
dir env: # Show all available variables
Macros / Built-in Variables
Variable | Description |
---|---|
$_ |
Either |
$? |
Success/failure of previous statement - see Basic_Error_Handler |
$Error |
Last error - array of recent errors - see $error |
$LastExitCode |
Exit code of the last natively run application |
$foreach |
Enumerator in a foreach loop |
$Host |
Information about the machine being executed on |
$args |
Array of arguments passed to a script - see Script_Arguments |