Saturday, December 30, 2006

Chronicles of a Script

In my networking environment, department documents are residing inside a root directory with a name of a four digit year number, one for each year. Underneath, there is a complex directory structure for a various kind of areas, and so on.

As the months go by, new files, directories and sub directories are added. With every beginning of a year, a new directory is created to accommodate the new year files.

Back then, I needed to make a new directory called 2006 and inside of it create the directory tree structure of the 2005 folder. After doing so, users will save their files in the new directory and will navigate their way in a familiar environment while trying to save their files.

This article describes the process as it evolved over time in three ways:

- VBScript
- PowerShell

So... i thought to my self, i can copy the 2005 folder to an alternate location, delete all the files in it, and then move the remaining skeleton directories underneath the 2006 folder.
I searched some more and found XCOPY. Running xcopy /? for help revealed its options and completed the solution.

I rejected the first option as being a little dirty and choosed to run XCOPY with some switches. Creating the 2007 folder took no more than a blink.

DOS version

The following XCOPY command will create the same directory structure of c:\windows under c:\testDir (and will create the c:\testDir directory if it doesn't exist).

XCOPY c:\windows c:\testDir /E /T /I

/E - Copies directories and subdirectories, including empty ones.
/T - Creates directory structure, but does not copy files.
/I - Will create the destination directory if it does not exist.

NOTE: if /I is omitted than the console will output this message:
Does C:\testDir specify a file name or directory name on the target(F = file, D = directory)?

This can be tricky if you plan to run the xcopy command inside VBScriptin a hidden window and wait for a return value. The script can hang as its waiting for user input.

VBScript version

A few days later i challenged my self to write a GUI version of it. One that will give me more control over of the process, let the user run it without requiring me to be on the spot and also save it for later use.

Finally, i decided to implement it as a context menu item when right-clicked up on a folder. So, when the user will right click a folder he will find an option called "Copy structure". When selected, the user will be presented with a BrowseForFolder common dialog box asking for the destination directory.

NOTE: To use the script just double-click it and then right-click a folder. You should see an option called Copy Structure, select it. It is best to move the script to a desired location where it wont be accidentally deleted. Then double-click it to update its location in the registry.

You can view the script here

PowerShell version

Finally, while a new year is entering and PS> is building itself as the next scripting environment, here is leveraged version as a one-liner PowerSheel command:

Get-ChildItem $env:windir -recurse | foreach { if($_.PSIsContainer) {New-Item ("d:\testingpath\"+$_.fullname.substring(3,$_.fullname.length-3)).ToString() -itemtype directory -force}}

this command will build the directory structure of c:\windows into d:\testingpath and will create d:\testingpath if it doesn't exist.

11.01.2007 Update:
After two weeks of try and error, here is a shorter one:

Copy-Item $env:windir -filter {$_.PSIsContainer} d:\testingpath -recurse -force

Happy structuring :-)


Thursday, December 28, 2006

Restart your engine - The PowerShell way

Frequently, I have the need to change the content of the PowerShell profile file Microsoft.PowerShell_profile.ps1) to reflect my needs. Any change to the profile requires me to exit PowerShell and re-launch it for changes to take place.

After making a lot of changes this can be very annoying. Well, this task definitely deserves a function!. The function will restart the console And will be stored in the profile for fast execution whenever you call it. But how? Can you restart any application from within itself ? After some experiments I came up with a solution that uses WScript.

Before delving into the solution, you must ensure the existence of a PowerShell profile file. PowerShell maintains a variable called $profile. Typing it into the console will reveal the file path. Note: although $profile will show you a path including the file name, this doesn’t mean that The profile really exists. Here are some commands you can use to find more info:

• test-path $profile # test if your profile exists
• if it returns false you should create it
• New-Item -path $profile -itemtype file -force

Now, The basic idea is to write a function in PowerShell that writes a .vbs file With some commands in it, to the temp directory. The file will use the WScript.Sleep function for a period of time and will then launch PowerShell.exe So lets start coding:

1. Launch PowerShell
2. type: notepad $profile to open your profile file
3. Copy and paste the following code and save changes:

# restarting PowerShell function res{
   $cmd = "Set oShell = CreateObject(""WScript.Shell"") `n"
   $cmd += "WScript.Sleep 1000 `n"
   $cmd += "oShell.Run ""PowerShell.exe""" | Out-File -filePath $env:temp"\ps.vbs" -inputobject  
   $cmd -encoding ASCII -force WScript.exe $env:temp"\ps.vbs"

4. Exit PowerShell From now on, for any change to the profile , and whenever you want to restart PowerShell just type res and watch it happen.