WMI Equivalent in UNIX and Linux

I like the whole WMI concept, and I could really make use of it under Linux ( in some scripts ). Is there something like that for Linux systems?

Not really. Are you using WMI to get system parameters, or to query processes, or to change configuration, or monitor for system events, or what?The kernel exposes a lot of information and tunable knobs via the /proc and /sys filesystems. No query language, just a organized hierarchy of directories and files. Some of these files are read-only, read-write, or write-only; some of them are pollable. 

Some services may have custom clients to query and update configuration on the fly — chrony’s chronyc comes to mind, but even the very most basic init has initctl. Newer services like HAL can be introspected and manipulated over D-Bus.

What Is /proc?

WIKIPEDIA: The proc file system acts as an interface to internal data structures in the kernel. It can be used to obtain information about the system and to change certain kernel parameters at runtime (sysctl).

Before we begin to talk about the proc filesystem as a programming facility, we need need to establish what it actually is. The proc filesystem is a pseudo-filesystem rooted at /proc that contains user-accessible objects that pertain to the runtime state of the  kernel and, by extension, the executing processes that run on top of it.

“Pseudo” is used because the proc filesystem exists only as a reflection of the in-memory kernel data structures it displays. This is why most files and directories within /proc are 0 bytes in size.

Broadly speaking, a directory listing of /proc reveals two main file groups. Each numerically named directory within /proc corresponds to the process ID (PID) of a process currently executing on the system. The following line of ls output illustrates this:

ls -ls /proc      
0 dr-xr-xr-x    3 root root           0 Apr 26 23:24 19636
0 dr-xr-xr-x  4 root    root             0 Feb 13 18:33 tty
0 -r--r--r--  1 root    root             0 Feb 13 18:33 uptime
0 -r--r--r--  1 root    root             0 Feb 13 18:33 version
0 -r--------  1 root    root             0 Feb 13 18:33 vmcore

Directory 19636 corresponds to PID 19636, a current bash shell session. These per-process directories contain both subdirectories and regular files that further elaborate on the runtime attributes of a given process. The proc(5) manual page discusses these process attributes at length.

The second file group within /proc is the non-numerically named directories and regular files that describe some aspect of kernel operation. As an example, the file /proc/version contains revision information relevant to the running kernel image.

Proc files are either read-only or read-write. The /proc/version file above is an example of a read-only file. Its contents are viewable by way of cat(1), and they remain static while the system is powered up and accessible to users. Read-write files, however, allow for both the display and modification of the runtime state of the kernel. /proc/sys/net/ipv4/ip_forwarding is one such file. Using cat(1) on this file reveals if the system is forwarding IP datagrams between network interfaces–the file contains a 1–or not–the file contains a 0. In echo(1)ing 1 or 0 to this file, that is, writing to the file, we can enable or disable the kernels ability to forward packets without having to build and boot a new kernel image. This works for many other proc files with read-write permissions.

Advertisements

What Is Active Directory? An understanding with VB Script.

Active Directory is a special-purpose database — it is not a registry replacement. The directory is designed to handle a large number of read and search operations and a significantly smaller number of changes and updates. Active Directory data is hierarchical, replicated, and extensible. Because it is replicated, you do not want to store dynamic data, such as corporate stock prices or CPU performance. If your data is machine-specific, store the data in the registry. Typical examples of data stored in the directory include printer queue data, user contact data, and network/computer configuration data. The Active Directory database consists of objects and attributes. Objects and attribute definitions are stored in the Active Directory schema.

You may be wondering what objects are currently stored in Active Directory. In Windows 2000, Active Directory has three partitions. These are also known as naming contexts: domain, schema, and configuration. The domain partition contains users, groups, contacts, computers, organizational units, and many other object types. Because Active Directory is extensible, you can also add your own classes and/or attributes. The schema partition contains classes and attribute definitions. The configuration partition includes configuration data for services, partitions, and sites.

An AD domain controller authenticates and authorizes all users and computers in a Windows domain type network—assigning and enforcing security policies for all computers and installing or updating software. For example, when a user logs into a computer that is part of a Windows domain, Active Directory checks the submitted password and determines whether the user is a system administrator or normal user.

Connecting to Active Directory

There are several methods used to access Active Directory. It is recommended that you use the ADSI API to access Active Directory. ADSI implements the LDAP protocol to communicate with Active Directory. The following code examples show how to access Active Directory.

Set ns = GetObject("LDAP:")

 This opens the LDAP provider and prepares it to retrieve data. No connection is established until data is requested. When data is requested, ADSI, with the help of the locator service, attempts to find the best domain controller (DC) for the connection and will establish a connection to the server. This process is known as serverless binding.

ADSI also enables you to specify the server name to use for the connection.

Set obj = GetObject("LDAP://mysrv01")

 In another scenario, you may only know the domain name but not the specific server name. Again, ADSI enables you to specify the domain name.

Set obj = GetObject("LDAP://fabrikam.com")

 Binding to Active Directory Objects

Before continuing with this scenario, you must understand how objects are named in Active Directory. A simple analogy for understanding the object naming system can be made by looking at how files are named within a file system. Each file has a name and a path. The file name must be unique among siblings. Consider the following example: “c:publicspecsadsi25.doc”. In this case, the file name is “adsi25.doc” and the file path is “c:publicspecsadsi25.doc”.

Objects in Active Directory are named in a similar manner. Every object has an object name or relative distinguished name (RDN), and an object path or distinguished name (DN). The RDN or object name has two parts: the attribute ID and the value itself. For example, “DC=Fabrikam”. DC is an RDN attribute ID and stands for domain component, and Fabrikam is the value of the attribute. In this scenario, the distinguished name of the Fabrikam domain object is “DC=Fabrikam,DC=Com”. A DN is composed of multiple RDNs.

In ADSI, the binding string is called the ADsPath. The ADsPath contains the service provider moniker, which identifies the type of service that is being used, followed by the distinguished name of the object. LDAP or WinNT are examples of service provider monikers. So an ADsPath looks like the following:

“LDAP://DC=Fabrikam,DC=Com”

Now, you can bind to the domain object as follows:

Set dom = GetObject("LDAP://DC=Fabrikam,DC=Com")

When you run this code example, ADSI uses the DN to determine which ADSI objects to bind to. When ADSI has bound to these objects, you can access all methods available on those objects. For example, the previous code example binds to IADs and IADsContainer. You can now use methods on those interfaces such as Get, Put, Create, Delete, MoveHere, and so on.

Creating an Organizational Unit

Now that you have the domain object, you can start creating organizational units. Fabrikam has two divisions: Sales and Production. The company is planning to hire two Windows 2000 administrators to manage each division. Joe Worden, as the enterprise administrator, will create two new organizational units under the Fabrikam domain. By creating an organizational unit, Joe can group multiple objects together and let someone else manage these objects. The following code example creates the Sales Organizational Unit (OU).

Dim dom as IADsContainer
Set dom = GetObject("LDAP://DC=Fabrikam,DC=Com")
Set salesOrg = dom.Create("organizationalUnit", "OU=Sales")
salesOrg.Put "description", "Sales Headquarter,SXR"
salesOrg.Put "wwwHomePage", "http://fabrikam.com/sales"
salesOrg.SetInfo

 The IADsContainer.Create method accepts the class name and the name of the new object. At this point the object is not committed to Active Directory. You, however, will have an ADSI/COM object reference on the client. With this ADSI object, you can set or modify attributes using the IADs.Put method. The IADs.Put method accepts the attribute name and the value of the attribute. Still, nothing is committed to the directory; everything is cached at the client. When you call the IADs.SetInfo method, the changes, in this case, object creation and attribute modification, are committed to the directory. These changes are transacted, meaning that you will see either the new object with all attributes you set, or no object at all.

You can also nest organizational units. The following code example assumes that the Sales division is divided further into the East and West regions.

Set east = salesOrg.Create("organizationalUnit", "OU=East")
east.SetInfo

This also applies for the West region.

Creating a New Group

The enterprise administrator, must create a new group. He would like to secure some resources, such as file, Active Directory objects, or other objects, based on the membership of this group. The following code example shows how to create a new group.

Set ou = GetObject("LDAP://OU=Sales,DC=Fabrikam,DC=COM")
Set grp = ou.Create("group", "CN=Management")
grp.Put "samAccountName", "mgmt"
grp.Put "groupType", ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP Or ADS_GROUP_TYPE_SECURITY_ENABLED
grp.SetInfo

 This group, Management, will be created in the Sales organizational unit.

  • First, Joe must create an ADSI object for the Sales organizational unit.
  • Second, he must set the samAccountName attribute on this object, because it is a mandatory attribute for backward compatibility. For this example, when samAccountName is set, Windows NT 4.0 tools such as User Manager see the attribute as mgmt instead of Management.
  • Third, Joe must specify the type of group. In a Windows 2000 domain, there are three types of groups: Global, Domain Local, and Universal. In addition, the group carries its security characteristic. A group can be either a security-enabled or a non-secured group. Essentially, security-enabled groups are those that can be granted or denied access rights to resources, similar to a user. Granting a group access to a file share, for example, implies that all members of the group can access the file share. Distribution lists cannot be used in a similar manner—you cannot, for example, grant a distribution list the right to access a file share. During the upgrade, Windows NT 4.0 groups are migrated as security-enabled groups. Non-secured groups in Active Directory are similar to distribution lists in Exchange. Hence, creating groups or distribution lists are similar operations in Windows 2000. In the Windows 2000 native mode (native mode means that all domain controllers in a domain are computers running Windows 2000 Server), the groups can be nested to any level.

Creating New Users in the Organizational Unit

Terry Adams was hired into the Fabrikam Sales organization. His direct report is Patrick Hines.

As shown in the following code example, Joe Andreshak, the enterprise administrator, will create a new account for him.

Dim salesOU as IADsContainer
Set salesOU = GetObject("LDAP://OU=Sales,DC=Fabrikam,DC=COM")
Set usr = salesOU.Create("user", "CN=Terry Adams")
usr.Put "sAMAccountName", "terryadams"
usr.Put "userPrincipalName", "terryadams@fabrikam.com" 
usr.Put "title" "Marketing Manager"
usr.SetInfo

usr.SetPassword "seahorse"
usr.AccountDisabled = False
usr.SetInfo

 When creating a new user, you must specify a sAMAccountName. This is a mandatory attribute for the user class. Before an instance of an object can be created, all mandatory attributes must be set. The user sAMAccountName is used to log on from computers running versions of Windows earlier than Windows 2000. Computers running Windows 2000 continue to understand the sAMAccountName. Beginning with Windows Server 2003, the sAMAccountName will automatically be generated if one is not specified for a new user.

In a Windows 2000 environment (both the client and the DC are running Windows 2000), the user can log on using either the sAMAccountName or userPrincipalName. In this example, Terry’s userPrincipalName is set to “terryadams@fabrikam.com”. If Terry moves to a different domain in the same enterprise, he can continue to use his userPrincipalName.

When creating a new user, all of the required attributes must be set in the local cache before the IADs.SetInfo method is called.

Joe, as an administrator, can assign Terry’s password using the IADsUser.SetPassword method. The IADsUser.SetPassword method will not work until the IADs.SetInfo method has been called.

Then, Joe enables the user account by setting the IADsUser.AccountDisabled property to FALSE.

The following code example shows how to set Terry as Patrick’s manager.

Set usr = GetObject("LDAP://CN=patrickhines, OU=Sales, DC=Fabrikam, DC=COM")
usr.Put "manager", "CN=Terry Adams,OU=Sales, DC=Fabrikam,DC=COM"
usr.SetInfo

You may wonder what happens if Terry changes his name, moves to a different organization, or leaves the company. Who maintains this manager-direct report link? For more information, and the solution to this problem, see Reorganization. Because the Active Directory schema is extensible, you can model your objects to include similar manager-direct report style relationships.

Before going on to the next task, look at a code example that shows how Joe would view Terry’s direct reports.

Set usr = GetObject("LDAP://CN=Terry Adams, OU=Sales, DC=Fabrikam, DC=COM")
reports = usr.GetEx ("directReports")

For each directReport in reports
    Debug.Print directReport
Next

In this code example, Patrick will display as Terry’s direct report, even though the directReports attribute was never modified. Active Directory does this automatically.

In the directory world, an attribute can have single or multiple values. Because directReports has multiple values, you can get this information by looking at the schema, it is easier to use the IADs.GetEx method, which returns an array of values regardless of whether single or multiple values are returned.

The Active Directory Users and Computers snap-in lets you view direct reports and manager relationships on the user’s property page.

Adding Users to a Group

Joe Worden, the enterprise administrator, will now add Julie Bankert to the Management group. To add an object to a group, use the IADsGroup.Add method.

Dim grp as IADsGroup

Set grp = GetObject("LDAP://CN=Management,OU=Sales,DC=Fabrikam,DC=COM") 
grp.Add ("LDAP://CN=Julie Bankert,OU=Sales,DC=Fabrikam,DC=COM")

Moving Existing Users to the Organizational Unit

When the enterprise administrator, Joe Worden, upgrades the Windows NT 4.0 domain to Active Directory, all users and groups are migrated to the Users containers in the Fabrikam domain. Joe can now move those users and groups to the appropriate organizational units. An object can also be moved between related Windows 2000 domains using ADSI.

In the following code example, Joe moves “jeffsmith” to the Sales organization.

Set usr = salesOU.MoveHere("LDAP://CN=jeffsmith,CN=Users,DC=fabrikam,DC=com", vbNullString)

The IADsContainer.MoveHere method takes the ADsPath of the object to be moved and the new object name (RDN). To keep the same name, you can specify NULL (vbNullString) for the bstrNewName parameter. To rename the object when it is moved, specify the new relative distinguished name for the bstrNewName parameter. For example, to move jeffsmith to the sales organization and rename the “jeffsmith” object to “jeff_smith” in the same operation, Joe would execute the following code:

Set usr = salesOU.MoveHere("LDAP://CN=jeffsmith,CN=Users,DC=fabrikam,DC=com", "CN=jeff_smith")

Adding Users to a Group

Joe Worden, the enterprise administrator, will now add Julie Bankert to the Management group. To add an object to a group, use the IADsGroup.Add method.

Dim grp as IADsGroup

Set grp = GetObject("LDAP://CN=Management,OU=Sales,DC=Fabrikam,DC=COM") 
grp.Add ("LDAP://CN=Julie Bankert,OU=Sales,DC=Fabrikam,DC=COM")

 Enumerating Objects

To view the child object of a container, such as an organizational unit (OU), enumerate the container object. To make an analogy to a file system, the child object would correspond to files in the directory, while the container, which is the parent object, would correspond to the directory itself. You may also use the enumerate operation when you want to get the parent object of an object.

When you enumerate an object, you actually bind to an object in the directory, and an IADs interface is returned on each object.

The following code example shows how to enumerate the children of a container.

Dim ou As IADs
' Bind to an object using its DN.
On Error GoTo Cleanup

Set ou = GetObject("LDAP://OU=Sales, DC=Fabrikam, DC=COM")

For each child in ou
    Debug.Print child.Name
Next

Cleanup:
    If (Err.Number<>0) Then
        MsgBox("An error has occurred. " & Err.Number)
    End If
    Set ou = Nothing

You can filter the types of objects returned from the enumeration. For example, to display only users and groups, use the following code example before the enumeration.

Ou.Filter = Array("user", "group")

If you have an object reference, you can get the object’s parent using the IADs Parent property. The following code example shows how to bind to the parent object.,pre>parentPath = obj.Parent
Set parent = GetObject(parentPath)

parentPath = obj.Parent
Set parent = GetObject(parentPath)

Searching for Objects

Julie Bankert must find telephone numbers for all Program Managers who work in Department 101. She can create a script that uses ADO and ADSI to accomplish this.

When using the ADO provider to perform a query, the result set will only return the first 1000 objects. For this reason, it is important to use a paged query so that you can retrieve all of the objects in the result set, as long as the directory service has not been set to limit the number of items in a result set. Return sets will contain the number of items that you specify in the page size, and paged sets will continue to be returned until all items in the result set have been returned.

' Create the connection and command object.
Set oConnection1 = CreateObject("ADODB.Connection")
Set oCommand1 = CreateObject("ADODB.Command")
' Open the connection.
oConnection1.Provider = "ADsDSOObject"  ' This is the ADSI OLE-DB provider name
oConnection1.Open "Active Directory Provider"
' Create a command object for this connection.
Set oCommand1.ActiveConnection = oConnection1

' Compose a search string.
oCommand1.CommandText = "select name,telephoneNumber " & _
"from 'LDAP://DC=Fabrikam,DC=com'" & _
"WHERE objectCategory='Person'" & _
"AND objectClass='user'" & _
"AND title='Program Manager'" & _
"AND department=101"

' Execute the query.
Set rs = oCommand1.Execute
'--------------------------------------
' Navigate the record set
'--------------------------------------
While Not rs.EOF
    Debug.Print rs.Fields("Name") & " , " & rs.Fields("telephoneNumber")
    rs.MoveNext
Wend

 To perform an ADSI search in Visual Basic or a scripting environment, three ADO components are required: Connection, Command, and Recordset. The Connection object enables you to specify the provider name, alternate credentials, if applicable, and other flags. The Command object enables you to specify search preferences and the query string. You must associate the Connection object to a Command object before the execution of the query. Finally, the Recordset object is used to iterate the result set.

ADSI supports two types of query strings or dialects. The preceding code example uses the SQL dialect. You can also use the LDAP dialect. The LDAP dialect query string is based on RFC 2254 (an RFC is a Request For Comments document, which is the basis for developing LDAP standards). The preceding example can be translated to the following code example.

oCommand1.CommandText = ";" & _
"(&(objectCategory=Person)" & _
"(objectClass=user)" & _
"(title=ProgramManager)" & _
"(department=101));" & _
"name,telephoneNumber;subTree"

Why is the word “subtree” at the end of the string? In the directory world, you can specify the scope of search. The choices are: “base”, “onelevel”, and “subtree”. “base” is used to read the object itself; “onelevel” refers to the immediate children, similar to the dir command; “subtree” is used to search deep or down multiple levels (similar to dir /s).

With the SQL dialect, you can specify scope in the command property, such as in the following code example.

oCommand1.Properties("SearchScope") = ADS_SCOPE_ONELEVEL

If scope is not specified, by default it uses a subtree search.

Reorganization

The sales organization has moved to a new organization — “Sales and Support.” Julie Bankert has been promoted to vice president and will lead the new organization. Joe Worden, the enterprise administrator, must move Sales OU to a new OU, Sales and Support.

Set dom = GetObject("LDAP://DC=Fabrikam, DC=COM")
Set salesSupport = dom.Create("organizationalUnit", "CN=Sales and Support")
Set sales = salesSupport.MoveHere("LDAP://OU=Sales, DC=Fabrikam, DC=COM", vbNullString)

With this code example, all objects in the sales organizational unit, including the sub-organizational units, are moved to the new organizational unit.

Now, Joe can move Julie into the Sales and Support organizational unit.

Set usr = salesSupport.MoveHere("LDAP://CN=Julie Bankert, OU=Sales, OU=Sales and Support, DC=Fabrikam,DC=COM")
usr.Put "title", "Vice President"
usr.SetInfo

Be aware that the manager-direct report link between Julie Bankert and Chris Gray is automatically updated by Active Directory.

 

Arrays and Hash Tables in PowerShell

Arrays and Hash Tables

No matter how many results a command returns, you can always store the results in a variable because of a clever trick. PowerShell automatically wraps results into an array when there is more than one result. In this chapter, you’ll learn how arrays work. You’ll also discover a special type of array, a hash table. While normal arrays use a numeric index to access their elements, hash tables use key-value-pairs.

PowerShell Commands Return Arrays

If you store the result of a command in a variable and then output it, you might at first think that the variable contains plain text:

$a = ipconfig
$a
Windows IP Configuration
Ethernet adapter LAN Connection
Media state
. . . . . . . . . . . : Medium disconnected
Connection-specific DNS Suffix:
Connection location IPv6 Address . : fe80::6093:8889:257e:8d1%8
IPv4 address . . . . . . . . . . . : 192.168.1.35
Subnet Mask . . . . . . . . . . . . : 255.255.255.0
Standard Gateway . . . . . . . . . . : 192.168.1.1

However, that’s not true. Each line is stored as a separate value in your variable and the variable is really an array. This happens automatically whenever a command returns more than one result.

Storing Results in Arrays

This is how you identify arrays:

$a = "Hello"
$a -is [Array]
False
$a = ipconfig
$a -is [Array]
True

If the result is an array, you can find the number of elements stored in it by using the Count property:

$a.Count
53

In this example, the ipconfig command returned 53 single results that are all stored in $a. If you’d like to examine a single array element, specify its index number. If an array has 53 elements, its valid index numbers are 0 to 52 (the index always starts at 0).

# Show the second element:
$a[1]

Whether or not the result is an array depends on the number of results that were returned. If more than one, PowerShell returns an array. Otherwise, it returns the result directly so the same command can behave differently from case to case, depending on the number of results.

$result = Dir
$result -is [array]

True

$result = Dir C:autoexec.bat
$result -is [array]

False
$result = @(Dir)
$result.Count

Use the construct @() if you’d like to force a command to always return its result in an array. This way the command will always return an array, even ifthe command returns only one result or none at all. This way you find out the number of files in a folder:

Or in a line:

@(Dir).Count

 Further Processing of Array Elements in a Pipeline:

ipconfig returns each line of text as array, enabling you to process them individually:

# Store result of an array and then pass along a pipeline to Select-String:
$result = ipconfig
$result | Select-String "Address"
Connection location IPv6 Address . . . : fe80::6093:8889:257e:8d1%8
IPv4 address . . . . . . . . . . . : 192.168.1.35
Connection location IPv6 Address . : fe80::5efe:192.168.1.35%16
Connection location IPv6 Address . . . : fe80::14ab:a532:a7b9:cd3a%11
# Everything in one line: output only lines including the
# word "address":
ipconfig | Select-String "Address"
Connection location IPv6 Address . . . : fe80::6093:8889:257e:8d1%8
IPv4-Adress . . . . . . . . . . . . : 192.168.1.35
Connection location IPv6 Address . . . : fe80::5efe:192.168.1.35%16
Connection location IPv6 Address . . . : fe80::14ab:a532:a7b9:cd3a%11

 The result of ipconfig was passed to Select-String, which is a text filter that allows only text lines that include the searched word through the PowerShell pipeline. With minimal effort, you can reduce the results of ipconfig to the information you find relevant.

Working with Real Objects in PowerShell

ipconfig is a legacy command, not a modern PowerShell cmdlet. While it is a command that returns individual information stored in arrays, this individual information consists of text. Real PowerShell cmdlets return rich objects, not text, even though this is not apparent at first:

Dir
Directory: Microsoft.PowerShell.CoreFileSystem::C:Users
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 10/01/2007 16:09 Application Data
d---- 07/26/2007 11:03 Backup
d-r-- 04/13/2007 15:05 Contacts
d---- 06/28/2007 18:33 Debug
d-r-- 10/04/2007 14:21 Desktop
d-r-- 10/04/2007 21:23 Documents
d-r-- 10/09/2007 12:21 Downloads
(...)

Let’s check if the return value is an array: 

$result = Dir
$result.Count
82

 Every element in an array represents a file or a directory. So if you output an element from the array to the console, PowerShell automatically converts the object back into text:

# Access the fifth element:
$result[4]
Directory: Microsoft.PowerShell.CoreFileSystem::C:Users
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r-- 04.10.2007 14:21 Desktop

You will realize that each element is much more than plain text when you pass it to the Format-List cmdlet and use an asterisk to see all of its properties:

# Display all properties of this element:
$result[4] | Format-List *
PSPath : Microsoft.PowerShell.CoreFileSystem::
C:UsersTobias WeltnerDesktop
PSParentPath : Microsoft.PowerShell.CoreFileSystem:: C:UsersTobias Weltner
PSChildName : Desktop
PSDrive : C
PSProvider : Microsoft.PowerShell.CoreFileSystem
PSIsContainer : True
Mode : d-r--
Name : Desktop
Parent : Tobias Weltner
Exists : True
Root : C:
FullName : C:UsersTobias WeltnerDesktop
Extension :
CreationTime : 04/13/2007 01:54:53
CreationTimeUtc : 04/12/2007 23:54:53
LastAccessTime : 10/04/2007 14:21:20
LastAccessTimeUtc : 10/04/2007 12:21:20
LastWriteTime : 10/04/2007 14:21:20
LastWriteTimeUtc : 10/04/2007 12:21:20
Attributes : ReadOnly, Directory

 Creating New Arrays in PowerShell

 You can create your own arrays, too. The easiest way is to use the comma operator:

$array = 1,2,3,4
$array
1234

Specify the single elements that you want to store in the array and then separate them by a comma. There’s even a special shortcut for sequential numbers:

$array = 1..4
$array
1234

Polymorphic Arrays

Just like variables, individual elements of an array can store any type of value you assign. This way, you can store whatever you want in an array, even a mixture of different data types. You can separate the elements using commas:

$array = "Hello", "World", 1, 2, (Get-Date)
$array
Hello
World
12
Tuesday, August 21, 2013 12:12:28

Why is the Get-Date cmdlet in the last example enclosed in parentheses?Just try it without parentheses. Arrays can only store data. Get-Date is a command and no data. Since you want PowerShell to evaluate the command first and then put its result into the array, you need to use parentheses. Parentheses identify a sub-expression and tell PowerShell to evaluate and process it first.

 Arrays With Only One (Or No) Element in PowerShell

  How do you create arrays with just one single element? Here’s how:

$array = ,1
$array.Length
1

You’ll need to use the construct

@(...)

to create an array without any elements at all:

$array = @()
$array.Length
0
$array = @(12)
$array
12
$array = @(1,2,3,"Hello")
$array
1
2
3
Hello

Addressing Array Elements in PowerShell:

Every element in an array is addressed using its index number. Negative index numbers count from last to first. You can also use expressions that calculate the index value:

# Create your own new array:
$array = -5..12
# Access the first element:
$array[0]
-5
# Access the last element (several methods):
$array[-1]
12
$array[$array.Count-1]
12
$array[$array.length-1]
12
# Access a dynamically generated array that is not stored in a variable:
(-5..12)[2]
-3

Remember, the first element in your array always has the index number 0. The index -1 will always give you the last element in an array. The example demonstrates that the total number of all elements will be returned in two properties: 

Count and Length. Both of these properties will behave identically.

Choosing Several Elements from an Array:

You can use square brackets to select multiple elements in an array. In doing that, you get a new array containing only the selected elements from the old array:

# Store directory listing in a variable:
$list = dir
# Output only the 2nd, 5th, 8th, and 13th entry: $list[1,4,7,12]
Directory: Microsoft.PowerShell.CoreFileSystem::C:Users
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 07/26/2007 11:03 Backup
d-r-- 08/20/2007 07:52 Desktop
d-r-- 08/12/2007 10:21 Favorites
d-r-- 04/13/2007 01:55 Saved Games

 The second line selects the second, fifth, eighth, and thirteenth elements (remember that the index begins at 0). You can use this approach to reverse the contents of an array: 

# Create an array with values from 1 to 10
$array = 1..10
# Select the elements from 9 to 0 (output array contents
# in reverse order):
$array = $array[($array.length-1)..0]
$array
10
9
...
1

Reversing the contents of an array

Uusing the approach (described above) is not particularly efficient because PowerShell has to store the result in a new array. Instead, you should use the special array functions of the .NET Framework. They enable you to reverse the contents of an array very efficiently:

# Create an array containing text and output contents:
$a = ipconfig
$a
# Reverse array contents and then output it again:
[array]::Reverse($a)
$a

Adding Elements to an Array and Removing Them

Arrays always contain a fixed number of elements. You’ll have to make a new copy of the array with a new size to add or remove elements later. You can simply use the “+=” operator to do that and then add new elements to an existing array:

# Add a new element to an existing array:
$array += "New Value"
$array
123
New Value

Array sizes can’t be modified so PowerShell will work behind the scenes to create a brand-new, larger array, copying the contents of the old array into it, and adding the new element. PowerShell works exactly the same way when you want to delete elements from an array. Here, too, the original array is copied to a new, smaller array while disposing of the old array. For example, the next line removes elements 4 and 5 using the indexes 3 and 4:

$array = $array[0..2] + $array[5..10]
$array.Count
9

Using Hash Tables

Hash tables store “key-value pairs.” So, in hash tables you do not use a numeric index to address individual elements, but rather the key you assigned to a value.

Creating a New Hash Table

To create a new hash table, use @{} instead of @(), and specify the key-value pair that is to be stored in your new hash table. Use semi-colons to separate key-value pairs:

# Create a new hash table with key-value pairs
$list = @{Name = "PC01"; IP="10.10.10.10"; User="Tobias Weltner"}
Name                   Value
----                   -----
Name                   PC01
IP                     10.10.10.10
User                   Tobias Weltner
# Access to the key "IP" returns the assigned value:
$list["IP"]
10.10.10.10
# As for arrays, several elements can be selected at the same time:
$list["Name", "IP"]
PC01
10.10.10.10 
# A key can also be specified by dot notation:
$list.IP
10.10.10.10
# A key can even be stored in a variable:
$key = "IP"
$list.$key
10.10.10.10
# Keys returns all keys in the hash table:
$list.keys
Name
IP
User
# If you combine this, you can output all values in the hash table
$list[$list.keys]
PC01
10.10.10.10
Tobias Weltner

The example shows that you retrieve the values in the hash table using the assigned key. There are two forms of notation you can use to do this:

  • Square brackets: Either you use square brackets, like in arrays;
  • Dot notation: Or you use dot notation, like with objects, and specify respectively the key name with the value you want to return. The key name can be specified as a variable.

The square brackets can return several values at the same time exactly like arrays if you specify several keys and separate them by a comma. Note that the key names in square brackets must be enclosed in quotation marks (you don’t have to do this if you use dot notation).

Storing Arrays in Hash Tables

You can store classic array inside of hash tables, too. This is possible because hash tables use the semi-colon as key-value-pair separators, leaving the comma available to create classic arrays:

# Create hash table with arrays as value:
$test = @{ value1 = 12; value2 = 1,2,3 }
# Return values (value 2 is an array with three elements):
$test.value1
12
$test.value2
1
2
3

 Inserting New Keys in an Existing Hash Table

If you’d like to insert new key-value pairs in an existing hash table, just specify the new key and the value that is to be assigned to the new key. Again, you can choose between the square brackets and dot notations.

# Create a new hash table with key-value pairs
$list = @{Name = "PC01"; IP="10.10.10.10"; User="Tobias Weltner"}
# Insert two new key-value pairs in the list (two different
# notations are possible):
$list.Date = Get-Date
$list["Location"] = "Hanover"
# Check result:
$list
Name Value
---- -----
Name PC01
Location Hanover
Date 08/21/2007 13:00:18
IP 10.10.10.10
User Tobias Weltner

 Because it’s easy to insert new keys in an existing hash table you can create empty hash tables and then insert keys as needed:

# Create empty hash table
$list = @{}
# Subsequently insert key-value pairs when required
$list.Name = "PC01"
$list.Location = "Hanover"
(...)

Modifying and Removing Values

If all you want to do is to change the value of an existing key in your hash table, just overwrite the value:

# Overwrite the value of an existing key with a new value (two possible notations):
$list["Date"] = (Get-Date).AddDays(-1)
$list.Location = "New York"
Name Value
---- -----
Name PC01
Location New York
Date 08/20/2007 13:10:12
IP 10.10.10.10
User Tobias Weltner

If you’d like to completely remove a key from the hash table, use Remove() and as an argument specify the key that you want to remove:

$list.remove("Date")

Using Hash Tables for Output Formatting

An interesting use for hash tables is to format text. Normally, PowerShell outputs the result of most commands as a table and internally uses the cmdlet Format-Table:

# Both lines return the same result:
Dir
Dir | Format-Table

If you use Format-Table, you can pass it a hash table with formatting specifications. This enables you to control how the result of the command is formatted. Every column is defined with its own hash table. In the hash table, values are assigned to the following four keys:

  • Expression: The name of object property to be displayed in this column.
  • Width: Character width of the column.
  • Label: Column heading.
  • Alignment: Right or left justification of the column

All you need to do is to pass your format definitions to Format-Table to ensure that your listing shows just the name and date of the last modification in two columns:

# Setting formatting specifications for each column in a hash table:
$column1 = @{expression="Name"; width=30; `
label="filename"; alignment="left"}
$column2 = @{expression="LastWriteTime"; width=40; `
label="last modification"; alignment="right"}
# Output contents of a hash table:
$column1
Name Value
---- -----
alignment left
label File name
width 30
expression Name
# Output Dir command result with format table and
# selected formatting:
Dir | Format-Table $column1, $column2
File Name Last Modification
--------- ---------------
Application Data 10/1/2007 16:09:57
Backup 07/26/2007 11:03:07
Contacts 04/13/2007 15:05:30
Debug 06/28/2007 18:33:29
Desktop 10/4/2007 14:21:20
Documents 10/4/2007 21:23:10
(...)

 Copying Arrays and Hash Tables

Copying arrays or hash tables from one variable to another works, but may produce unexpected results. The reason is that arrays and hash tables are not stored directly in variables, which always store only a single value. When you work with arrays and hash tables, you are dealing with a reference to the array or hash table. So, if you copy the contents of a variable to another, only the reference will be copied, not the array or the hash table. That could result in the following unexpected behavior:

$array1 = 1,2,3
$array2 = $array1
$array2[0] = 99
$array1[0]
99

 Although the contents of $array2 were changed in this example, this affects $array1 as well, because they are both identical. The variables $array1 and $array2 internally reference the same storage area. Therefore, you have to create a copy if you want to copy arrays or hash tables,:

$array1 = 1,2,3
$array2 = $array1.Clone()
$array2[0] = 99
$array1[0]
1

Whenever you add new elements to an array (or a hash table) or remove existing ones, a copy action takes place automatically in the background and its results are stored in a new array or hash table. The following example clearly shows the consequences:

# Create array and store pointer to array in $array2:
$array1 = 1,2,3
$array2 = $array1
# Assign a new element to $array2. A new array is created in the process and stored
in $array2:
$array2 += 4
$array2[0]=99
# $array1 continues to point to the old array:
$array1[0]
1

Strongly Typed Arrays

Arrays are typically polymorphic: you can store any type of value you want in any element. PowerShell automatically selects the appropriate type for each element. If you want to limit the type of data that can be stored in an array, use “strong typing” and predefine a particular type. You should specify the desired variable type in square brackets. You also specify an open and closed square bracket behind the variable type because this is an array and not a normal variable:

# Create a strongly typed array that can store whole numbers only:
[int[]]$array = 1,2,3
# Everything that can be converted into a number is allowed
# (including strings):
$array += 4
$array += 12.56
$array += "123"
# If a value cannot be converted into a whole number, an error
# will be reported:
$array += "Hello"
The value "Hello" cannot be converted into the type "System.Int32".
Error: "Input string was not in a correct format."
At line:1 char:6
+ $array <<<< += "Hello"

In the example, $array was defined as an array of the Integer  type. Now, the array is able to store only whole numbers. If you try to store values in it that cannot be turned into whole numbers, an error will be reported.

Summary

Arrays and hash tables can store as many separate elements as you like. Arrays assign a sequential index number to elements that always begin at 0. Hash tables in contrast use a key name. That’s why every element in hash tables consists of a key-value pair.

You create new arrays with @(Element1, Element2, …). You can also leave out @() for arrays and only use the comma operator. You create new hash tables with @{key1=value1;key2=value2; …). @{} must always be specified for hash tables. Semi-colons by themselves are not sufficient to create a new hash table. You can address single elements of an array or hash able by using square brackets. Specify either the index number (for arrays) or the key (for hash tables) of the desired element in the square brackets. Using this approach you can select and retrieve several elements at the same time.

Powered by WordPress.com.

Up ↑