Friday, August 28, 2009

Get or set file\directory attributes using Powershell

This is just “reminder post” for me if I will need to use it in future…

I wanted to create hidden folder using Powershell – in fact, it’s not very hard:

$(MkDir “Martin Zugec”).Attributes = ‘Hidden’

.Attributes is FileAttributes enumeration, so you use same syntax when you want to create files. Below are available values:

Archive Applications use this attribute to mark file\folder for backup or removal
Compressed Is compressed
Device N/A – in future maybe ;) Looks promising :)
Directory “File is directory” :)
Encrypted Encrypted
Hidden Hidden
Normal If no other attributes are applied
NotContentIndexed Skip for indexing
Offline File\Folder is offline
ReadOnly File\Folder is read-only
ReparsePoint Contains reparse point. Reparse point is user data associated with this entry – one example where reparse points are used is when you mount folder into another.
SparseFile Sparse files are usually large files whose data are mostly zeros. To be honest, I never saw sparse file as far as I know – even fake huge files (FSUtil File CreateNew) are not sparse files.
System System file
Temporary If file is marked as temporary, file system will try to keep all the data in memory for quicker access instead of flushing data back to harddisk. Of course temporary file should be deleted as soon as possible if not needed ;)

If you want to change attribute of existing folder\file, it’s also very easy:

$(Get-Item “Martin Zugec”).Attributes = ‘Hidden’

Wednesday, August 26, 2009

How to return multiple values from Powershell function

I used this trick few times when I had function that had to return more values…

As I wrote here, you can access dictionary object using syntax $Dictionary.Key… If your function returns dictionary, then you can use it to your advantage :)

   1: Function Get-MultiValue () { 



   2:  [hashtable]$Return = @{} 



   3:  



   4:  $Return.Success = $True 



   5:  $Return.PercentComplete = 100 



   6:  $Return.ReturnTime = $(Get-Date) 



   7:  $Return.Username = "Martin Zugec" 



   8:  



   9:  Return $Return 



  10: }




Usage is pretty obvious:





   1: $Var = Get-MultiValue 



   2:  



   3: If ($Var.Success) { 



   4:     $Var.UserName 



   5:     $Var.ReturnTime 



   6: }




Easy and useful sometimes…

Monday, August 24, 2009

New-Enum for Powershell v2

Recently I wrote about new-enum function that should work both in Posh v1 and v2… Well, it doesn’t, because v1 will throw an error on @Args.

Below is updated version that should work in both powershells:

# Supports creation of custom Enums during runtime. 
# Because CoreFunctions and CustomFunctions requires already some enumerations (specifically LogType), it must be initialized in main script.
function Global:S4M\New-Enum ([string] $name, [switch]$FixMode, [array]$Members = @()) {

$Members += $Args

$appdomain = [System.Threading.Thread]::GetDomain()
$assembly = new-object System.Reflection.AssemblyName
$assembly.Name = "EmittedEnum"
$assemblyBuilder = $appdomain.DefineDynamicAssembly($assembly,
[System.Reflection.Emit.AssemblyBuilderAccess]::Save -bor [System.Reflection.Emit.AssemblyBuilderAccess]::Run);
$moduleBuilder = $assemblyBuilder.DefineDynamicModule("DynamicModule", "DynamicModule.mod");
$enumBuilder = $moduleBuilder.DefineEnum($name, [System.Reflection.TypeAttributes]::Public, [System.Int32]);

for($i = 0; $i -lt $Members.Length; $i++) {
If (([string]($Members[$i])).Contains("=")) {
[string]$EnumName = [string](($Members[$i].Split("="))[0])
$Null = $enumBuilder.DefineLiteral($EnumName, [int]($Members[$i].Split("="))[1]);
} Else {
$Null = $enumBuilder.DefineLiteral($Members[$i], $i);
}
}

$enumBuilder.CreateType() > $Null;

#Used to fix issue with Powershell v2
If ($Host.Version.Major -eq 1 -or $FixMode) {
$enumBuilder.CreateType() > $Null;
} Else {
S4M\New-Enum -FixMode -Name $name -Members $Members
$enumBuilder.CreateType() > $Null;
}
}

Friday, August 14, 2009

Bug in PowerShell?

Yesterday I wrote about a way how to use [HashTable] type as single storage for different values… Well, code has to be changed a bit.

Remember that you can access member by using $People.$Foo? But it doesn’t work always, so you should use $People.[string]$Foo instead.

Have a look at following code:

[hashtable]$Collection = @{}
For ($i = 0; $i -lt 10; $i += 1) {$Collection.$i = $i}



Looks fine, doesn’t it? Well, let’s have a look at keys:



PS C:\> $Collection.Keys
9
8
7
6
5
4
3
2
1
0



Still ok. Now try to retrieve value for one of the keys:



PS C:\> $Collection.9
Unexpected token '.9' in expression or statement.
At line:1 char:14
+ $Collection.9 <<<<
+ CategoryInfo : ParserError: (.9:String) [], ParentContainsError
RecordException
+ FullyQualifiedErrorId : UnexpectedToken



Hmmmmmm, error. Let’s have a look at key type:



PS C:\> $Collection.Keys | ForEach {$_.GetType()}

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType



Have you noticed something? Key should be always string, however in this case, PowerShell doesn’t convert it automatically. Surprisingly, this allows us to have 2 keys with same name – something that should never happen ;)



PS C:\> $Collection."9" = 9
PS C:\> $Collection.Keys
9
9
8



Ok, and here comes solution. Whenever you assign key, specify that it is string:



[hashtable]$Collection = @{}
For ($i = 0; $i -lt 10; $i += 1) {$Collection.[string]$i = $i}

Create System.Array in PowerShell

Some months ago I posted following question:

I am working with MFCOM (COM interface for Citrix) and one of
properties requires Array as input type...

So I tried simple code, [Array]$Schedules = @(), however to my
surprise System.Array is only base object (never realized that
before).

# [array]$Schedules = @()
# $Schedules.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]
System.Array

If I try to assign this value to COM, it will return error message
stating that input type is not supported.

Obviously, no answers so far, just Gerd Schneider confirmed that this is an issue.

Long story short, it can be done pretty easily:

[Object[]]$Schedules = @()

Of course, you can replace Object with your own type:
[Byte[]]$Bytes = $Source.ToCharArray()

Thursday, August 13, 2009

Easy database for Powershell

I run into situation with my current project where I need to have easy to use translation table. Nothing too complicated – just be able to search for PackageID (we are talking about SCCM) and retrieve location of that package.

Ideal candidate for such tasks is HashTable or dictionary – each entry in hashtable contains Key and Value. Key is unique identifier of that entry and allows you to easily retrieve value.

Initialization of hashtable is very simple:
[HashTable]$People = @{}

Also adding members is easy, either powershell way:
$People += “Martin” = “Zugec”
or using .NET approach:
$People.Add(“Martin”, “Zugec”)

In order to retrieve surname, easiest method is to used built-in functionality of Posh – it makes it very easy for you:
$People.Martin

Returned value is Zugec.

Ok, so far pretty easy. However I realized that for my project I don’t need only surname (package location), but also company. As I said before – hashtable is collection of key\value pairs and key must be unique. Well, value doesn’t need to be single value ;)

$People += @{“Martin Zugec” = @{“Name” = “Martin”; “Surname” = “Zugec”; “Company” = “Login Consultants”}}

In this case, I decided to use hashtable as value of hashtable. I really like the way how complicated stuff can be done easily using Powershell :)

To retrieve company, I can use $People.”Martin Zugec”.Company

Pretty useful sometimes