CodePlexProject Hosting for Open Source Software
I have some older hardware running Windows 7 Media Center. Normally we let the Media Center just go to sleep on its own but I’ve been concerned that it isn’t sleeping as much during there wee hours of the morning … Continue reading →
If were following good security practices we run our Windows system with UAC enabled. This means that if you forget to launch your PowerShell prompt as Administrator when you run a script that requires administrative privilege then that script will … Continue reading →
You can download it here.
Oisin and I have been busy prepping the PowerShell Community Extensions to support Windows PowerShell 3.0. With this release, we are providing two packages. There is a Pscx-2.1.0-RC.zip that is xcopy deployable just like PSCX 2.0. Just remember to unblock … Continue reading →
We’ve just released a beta of the PowerShell Community Extensions 3.0 which targets PowerShell 3.0 specifically. This new version uses a WiX based installer. We may look at providing an xcopy deployable ZIP file but we had so many users … Continue reading →
PowerShell V3 now supports the ObsoleteAttribute for compiled cmdlets but unfortunately not advanced functions. This is handy to let your users know that a binary cmdlet will be going away in a future release of your binary module. As we … Continue reading →
One of the many new features in Windows PowerShell V3 is better support for alternate data streams (ADS) in NTFS files. ADS allows an NTFS file to contain additional data that is not part of the “main” stream i.e. the … Continue reading →
Within PowerShell it has always been easy to pass “simple” arguments to an EXE e.g.: C:\PS> ipconfig -all However passing arguments to certain exes can become surprising difficult when their command line parameter syntax is complex i.e. they require quotes … Continue reading →
You can grab the bits from here. If you have V3 CTP1 installed, please uninstall it first or you can get your machine into a bad state. So far my favorite two features new to this drop are both in … Continue reading →
Windows PowerShell version 3 introduces a simplified syntax for the Where-Object and Foreach-Object cmdlets. The simplified syntax shown below, eliminates the curly braces as well as the need for the special variable $_. C:\PS> Get-Process | Where PM -gt 100MB … Continue reading →
I just uploaded beta 1 for the PowerShell Community Extensions version 2.1. This beta drop adds better support for Windows PowerShell V3 that is in the Windows 8 Developer Preview. There are a number of bug fixes in this drop: … Continue reading →
Testing out my first WordPress blog post after the switch from Windows Live Spaces (sniff, I will miss you) to WordPress. Regarding the MVP Summit last week, I can’t really talk about much due to just about everything being NDA, NDA, … Continue reading →
Occasionally folks want to be able to create an EXE from PoweShell. PowerShell can’t do this by itself but this can be done with PowerShell script. Essentially what you can do is create a simple console EXE program that embeds the script as a resource and the EXE, upon loading retrieves the script and throws it at a PowerShell runspace to execute. Here’s the script for a feasibility test of doing this very thing.
Note that this script depends on Write-GZip from the PowerShell Community Extensions.
#requires -version 2.0<#.SYNOPSIS Creates an EXE wrapper from a PowerShell script by compressing the script and embedding into a newly generated assembly..DESCRIPTION Creates an EXE wrapper from a PowerShell script by compressing the script and embedding into a newly generated assembly..PARAMETER Path The path to the ..PARAMETER LiteralPath Specifies a path to one or more locations. Unlike Path, the value of LiteralPath is used exactly as it is typed. No characters are interpreted as wildcards. If the path includes escape characters, enclose it in single quotation marks. Single quotation marks tell Windows PowerShell not to interpret any characters as escape sequences..PARAMETER OutputAssembly The name (including path) of the EXE to generate..PARAMETER IconPath The path to an optional icon to be embedded as the application icon for the EXE..EXAMPLE C:\PS> .\Make-PS1ExeWrapper.ps1 .\MyScript.ps1 .\MyScript.exe .\app.ico This creates an console application called MyScript.exe that internally hosts the PowerShell engine and runs the script specified by MyScript.ps1. Optionally the file app.ico is embedded into the EXE as the application's icon..NOTES Author: Keith Hill Date: Aug 7, 2010 Issues: This implementation is more of a feasibility test and isn't fully functional. It doesn't support an number of PSHostUserInterface members as well as a number of PSHostRawUserInterface members. This approach also suffers from the same problem of running script "interactively" and not loading it from a file. That is, the entire script output is run through Out-Default and PowerShell gets confused. It formats the first types it sees correctly but after that the formatting is off. To correct this, you have to append | Out-Default where you script outputs to the host without using a Write-* cmdlet e.g.: MyScript.ps1: ------------------------------- Get-Process svchost Get-Date | Out-Default Dir C:\ | Out-Default Dir c:\idontexist | Out-Default $DebugPreference = 'Continue' $VerbosePreference = 'Continue' Write-Host "host" Write-Warning "warning" Write-Verbose "verbose" Write-Debug "debug" Write-Error "error"#>[CmdletBinding(DefaultParameterSetName="Path")]param( [Parameter(Mandatory=$true, Position=0, ParameterSetName="Path", ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, HelpMessage="Path to bitmap file")] [ValidateNotNullOrEmpty()] [string[]] $Path, [Alias("PSPath")] [Parameter(Mandatory=$true, Position=0, ParameterSetName="LiteralPath", ValueFromPipelineByPropertyName=$true, HelpMessage="Path to bitmap file")] [ValidateNotNullOrEmpty()] [string[]] $LiteralPath,
[Parameter(Mandatory = $true, Position = 1)] [string] $OutputAssembly, [Parameter(Position = 2)] [string] $IconPath)
Begin { Set-StrictMode -Version latest $src = @'using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Globalization;using System.IO;using System.IO.Compression;using System.Management.Automation;using System.Management.Automation.Host;using System.Management.Automation.Runspaces;using System.Reflection;using System.Security;using System.Text;using System.Threading;
namespace PS1ToExeTemplate{ class Program { private static object _powerShellLock = new object(); private static readonly Host _host = new Host(); private static PowerShell _powerShellEngine;
static void Main(string[] args) { Console.CancelKeyPress += Console_CancelKeyPress; Console.TreatControlCAsInput = false;
string script = GetScript(); RunScript(script, args, null); }
private static string GetScript() { string script = String.Empty;
Assembly assembly = Assembly.GetExecutingAssembly(); using (Stream stream = assembly.GetManifestResourceStream("Resources.Script.ps1.gz")) { var gZipStream = new GZipStream(stream, CompressionMode.Decompress, true); var streamReader = new StreamReader(gZipStream); script = streamReader.ReadToEnd(); }
return script; }
private static void RunScript(string script, string[] args, object input) { lock (_powerShellLock) { _powerShellEngine = PowerShell.Create(); }
try { _powerShellEngine.Runspace = RunspaceFactory.CreateRunspace(_host); _powerShellEngine.Runspace.Open(); _powerShellEngine.AddScript(script); _powerShellEngine.AddCommand("Out-Default"); _powerShellEngine.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
if (input != null) { _powerShellEngine.Invoke(new[] { input }); } else { _powerShellEngine.Invoke(); } } finally { lock (_powerShellLock) { _powerShellEngine.Dispose(); _powerShellEngine = null; } } }
private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) { try { lock (_powerShellLock) { if (_powerShellEngine != null && _powerShellEngine.InvocationStateInfo.State == PSInvocationState.Running) { _powerShellEngine.Stop(); } } e.Cancel = true; } catch (Exception ex) { _host.UI.WriteErrorLine(ex.ToString()); } } }
class Host : PSHost { private PSHostUserInterface _psHostUserInterface = new HostUserInterface();
public override void SetShouldExit(int exitCode) { Environment.Exit(exitCode); }
public override void EnterNestedPrompt() { throw new NotImplementedException(); }
public override void ExitNestedPrompt() { throw new NotImplementedException(); }
public override void NotifyBeginApplication() { }
public override void NotifyEndApplication() { }
public override string Name { get { return "PSCX-PS1ToExeHost"; } }
public override Version Version { get { return new Version(1, 0); } }
public override Guid InstanceId { get { return new Guid("E4673B42-84B6-4C43-9589-95FAB8E00EB2"); } }
public override PSHostUserInterface UI { get { return _psHostUserInterface; } }
public override CultureInfo CurrentCulture { get { return Thread.CurrentThread.CurrentCulture; } }
public override CultureInfo CurrentUICulture { get { return Thread.CurrentThread.CurrentUICulture; } } }
class HostUserInterface : PSHostUserInterface, IHostUISupportsMultipleChoiceSelection { private PSHostRawUserInterface _psRawUserInterface = new HostRawUserInterface();
public override PSHostRawUserInterface RawUI { get { return _psRawUserInterface; } }
public override string ReadLine() { return Console.ReadLine(); }
public override SecureString ReadLineAsSecureString() { throw new NotImplementedException(); }
public override void Write(string value) { string output = value ?? "null"; Console.Write(output); }
public override void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value) { string output = value ?? "null"; var origFgColor = Console.ForegroundColor; var origBgColor = Console.BackgroundColor; Console.ForegroundColor = foregroundColor; Console.BackgroundColor = backgroundColor; Console.Write(output); Console.ForegroundColor = origFgColor; Console.BackgroundColor = origBgColor; }
public override void WriteLine(string value) { string output = value ?? "null"; Console.WriteLine(output); }
public override void WriteErrorLine(string value) { string output = value ?? "null"; var origFgColor = Console.ForegroundColor; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(output); Console.ForegroundColor = origFgColor; }
public override void WriteDebugLine(string message) { WriteYellowAnnotatedLine(message, "DEBUG"); }
public override void WriteVerboseLine(string message) { WriteYellowAnnotatedLine(message, "VERBOSE"); }
public override void WriteWarningLine(string message) { WriteYellowAnnotatedLine(message, "WARNING"); }
private void WriteYellowAnnotatedLine(string message, string annotation) { string output = message ?? "null"; var origFgColor = Console.ForegroundColor; var origBgColor = Console.BackgroundColor; Console.ForegroundColor = ConsoleColor.Yellow; Console.BackgroundColor = ConsoleColor.Black; WriteLine(String.Format(CultureInfo.CurrentCulture, "{0}: {1}", annotation, output)); Console.ForegroundColor = origFgColor; Console.BackgroundColor = origBgColor; }
public override void WriteProgress(long sourceId, ProgressRecord record) { throw new NotImplementedException(); }
public override Dictionary<string, PSObject> Prompt(string caption, string message, Collection<FieldDescription> descriptions) { if (String.IsNullOrEmpty(caption) && String.IsNullOrEmpty(message) && descriptions.Count > 0) { Console.Write(descriptions[0].Name + ": "); } else { this.Write(ConsoleColor.DarkCyan, ConsoleColor.Black, caption + "\n" + message + " "); } var results = new Dictionary<string, PSObject>(); foreach (FieldDescription fd in descriptions) { string[] label = GetHotkeyAndLabel(fd.Label); this.WriteLine(label[1]); string userData = Console.ReadLine(); if (userData == null) { return null; }
results[fd.Name] = PSObject.AsPSObject(userData); }
return results; }
public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName) { throw new NotImplementedException(); }
public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options) { throw new NotImplementedException(); }
public override int PromptForChoice(string caption, string message, Collection<ChoiceDescription> choices, int defaultChoice) { // Write the caption and message strings in Blue. this.WriteLine(ConsoleColor.Blue, ConsoleColor.Black, caption + "\n" + message + "\n");
// Convert the choice collection into something that is // easier to work with. See the BuildHotkeysAndPlainLabels // method for details. string[,] promptData = BuildHotkeysAndPlainLabels(choices);
// Format the overall choice prompt string to display. var sb = new StringBuilder(); for (int element = 0; element < choices.Count; element++) { sb.Append(String.Format(CultureInfo.CurrentCulture, "|{0}> {1} ", promptData[0, element], promptData[1, element])); }
sb.Append(String.Format(CultureInfo.CurrentCulture, "[Default is ({0}]", promptData[0, defaultChoice]));
// Read prompts until a match is made, the default is // chosen, or the loop is interrupted with ctrl-C. while (true) { this.WriteLine(sb.ToString()); string data = Console.ReadLine().Trim().ToUpper(CultureInfo.CurrentCulture);
// If the choice string was empty, use the default selection. if (data.Length == 0) { return defaultChoice; }
// See if the selection matched and return the // corresponding index if it did. for (int i = 0; i < choices.Count; i++) { if (promptData[0, i] == data) { return i; } }
this.WriteErrorLine("Invalid choice: " + data); } }
#region IHostUISupportsMultipleChoiceSelection Members
public Collection<int> PromptForChoice(string caption, string message, Collection<ChoiceDescription> choices, IEnumerable<int> defaultChoices) { this.WriteLine(ConsoleColor.Blue, ConsoleColor.Black, caption + "\n" + message + "\n");
string[,] promptData = BuildHotkeysAndPlainLabels(choices);
var sb = new StringBuilder(); for (int element = 0; element < choices.Count; element++) { sb.Append(String.Format(CultureInfo.CurrentCulture, "|{0}> {1} ", promptData[0, element], promptData[1, element])); }
var defaultResults = new Collection<int>(); if (defaultChoices != null) { int countDefaults = 0; foreach (int defaultChoice in defaultChoices) { ++countDefaults; defaultResults.Add(defaultChoice); }
if (countDefaults != 0) { sb.Append(countDefaults == 1 ? "[Default choice is " : "[Default choices are "); foreach (int defaultChoice in defaultChoices) { sb.AppendFormat(CultureInfo.CurrentCulture, "\"{0}\",", promptData[0, defaultChoice]); } sb.Remove(sb.Length - 1, 1); sb.Append("]"); } }
this.WriteLine(ConsoleColor.Cyan, ConsoleColor.Black, sb.ToString());
var results = new Collection<int>(); while (true) { ReadNext: string prompt = string.Format(CultureInfo.CurrentCulture, "Choice[{0}]:", results.Count); this.Write(ConsoleColor.Cyan, ConsoleColor.Black, prompt); string data = Console.ReadLine().Trim().ToUpper(CultureInfo.CurrentCulture);
if (data.Length == 0) { return (results.Count == 0) ? defaultResults : results; }
for (int i = 0; i < choices.Count; i++) { if (promptData[0, i] == data) { results.Add(i); goto ReadNext; } }
#endregion
private static string[,] BuildHotkeysAndPlainLabels(Collection<ChoiceDescription> choices) { // Allocate the result array string[,] hotkeysAndPlainLabels = new string[2, choices.Count];
for (int i = 0; i < choices.Count; ++i) { string[] hotkeyAndLabel = GetHotkeyAndLabel(choices[i].Label); hotkeysAndPlainLabels[0, i] = hotkeyAndLabel[0]; hotkeysAndPlainLabels[1, i] = hotkeyAndLabel[1]; }
return hotkeysAndPlainLabels; }
private static string[] GetHotkeyAndLabel(string input) { string[] result = new string[] { String.Empty, String.Empty }; string[] fragments = input.Split('&'); if (fragments.Length == 2) { if (fragments[1].Length > 0) { result[0] = fragments[1][0].ToString(). ToUpper(CultureInfo.CurrentCulture); }
result[1] = (fragments[0] + fragments[1]).Trim(); } else { result[1] = input; }
return result; } }
class HostRawUserInterface : PSHostRawUserInterface { public override KeyInfo ReadKey(ReadKeyOptions options) { throw new NotImplementedException(); }
public override void FlushInputBuffer() { }
public override void SetBufferContents(Coordinates origin, BufferCell[,] contents) { throw new NotImplementedException(); }
public override void SetBufferContents(Rectangle rectangle, BufferCell fill) { throw new NotImplementedException(); }
public override BufferCell[,] GetBufferContents(Rectangle rectangle) { throw new NotImplementedException(); }
public override void ScrollBufferContents(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill) { throw new NotImplementedException(); }
public override ConsoleColor ForegroundColor { get { return Console.ForegroundColor; } set { Console.ForegroundColor = value; } }
public override ConsoleColor BackgroundColor { get { return Console.BackgroundColor; } set { Console.BackgroundColor = value; } }
public override Coordinates CursorPosition { get { return new Coordinates(Console.CursorLeft, Console.CursorTop); } set { Console.SetCursorPosition(value.X, value.Y); } }
public override Coordinates WindowPosition { get { return new Coordinates(Console.WindowLeft, Console.WindowTop); } set { Console.SetWindowPosition(value.X, value.Y); } }
public override int CursorSize { get { return Console.CursorSize; } set { Console.CursorSize = value; } }
public override Size BufferSize { get { return new Size(Console.BufferWidth, Console.BufferHeight); } set { Console.SetBufferSize(value.Width, value.Height); } }
public override Size WindowSize { get { return new Size(Console.WindowWidth, Console.WindowHeight); } set { Console.SetWindowSize(value.Width, value.Height); } }
public override Size MaxWindowSize { get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); } }
public override Size MaxPhysicalWindowSize { get { return new Size(Console.LargestWindowWidth, Console.LargestWindowHeight); } }
public override bool KeyAvailable { get { return Console.KeyAvailable; } }
public override string WindowTitle { get { return Console.Title; } set { Console.Title = value; } } }}'@}
Process { if ($psCmdlet.ParameterSetName -eq "Path") { # In the -Path (non-literal) case we may need to resolve a wildcarded path $resolvedPaths = @($Path | Resolve-Path | Convert-Path) } else { # Must be -LiteralPath $resolvedPaths = @($LiteralPath | Convert-Path) } foreach ($rpath in $resolvedPaths) { Write-Verbose "Processing $rpath"
$gzItem = Get-ChildItem $rpath | Write-GZip -Quiet $resourcePath = "$($gzItem.Directory)\Resources.Script.ps1.gz" if (Test-Path $resourcePath) { Remove-Item $resourcePath } Rename-Item $gzItem $resourcePath # Configure the compiler parameters $referenceAssemblies = 'System.dll',([psobject].Assembly.Location) $outputPath = $OutputAssembly if (![IO.Path]::IsPathRooted($outputPath)) { $outputPath = [io.path]::GetFullPath((Join-Path $pwd $outputPath)) } if ($rpath -eq $outputPath) { throw 'Oops, you don''t really want to overwrite your script with an EXE.' }
$cp = new-object System.CodeDom.Compiler.CompilerParameters $referenceAssemblies,$outputPath,$true $cp.TempFiles = new-object System.CodeDom.Compiler.TempFileCollection ([IO.Path]::GetTempPath()) $cp.GenerateExecutable = $true $cp.GenerateInMemory = $false $cp.IncludeDebugInformation = $true if ($IconPath) { $rIconPath = Resolve-Path $IconPath $cp.CompilerOptions = " /win32icon:$rIconPath" } [void]$cp.EmbeddedResources.Add($resourcePath) # Create the C# codedom compiler $dict = new-object 'System.Collections.Generic.Dictionary[string,string]' $dict.Add('CompilerVersion','v3.5') $provider = new-object Microsoft.CSharp.CSharpCodeProvider $dict # Compile the source and report errors $results = $provider.CompileAssemblyFromSource($cp, $src) if ($results.Errors.Count) { $errorLines = "" foreach ($error in $results.Errors) { $errorLines += "`n`t" + $error.Line + ":`t" + $error.ErrorText } Write-Error $errorLines } } }
We had some boiler plate code that we always put into our scripts to set strict mode and to compute $ScriptDir so the script can load other scripts relatively to its location. This boiler plate code is simple:
#requires –Version 2.0Set-StrictMode –Version 2.0$ScriptDir = Split-Path $MyInvocation.MyCommand.Path –Parent
However lets say you do this in all your scripts and one script (Parent.ps1) dot-sources another script (PoshLib.ps1) that does the same trick e.g.:
Parent.ps1:$ScriptDir = Split-Path $MyInvocation.MyCommand.Path –Parent"PARENT: Before dot-sourcing libary ScriptDir is $ScriptDir". $ScriptDir\Bin\PoshLib.ps1"PARENT: After dot-sourcing libary ScriptDir is $ScriptDir"
PoshLib.ps1$ScriptDir = Split-Path $MyInvocation.MyCommand.Path –Parent"POSHLIB: ScriptDir is $ScriptDir"
Seems innocent enough but check out the output of running Parent.ps1:
PS C:\Users\Keith> C:\Users\Keith\Parent.ps1PARENT: Before dot-sourcing libary ScriptDir is C:\Users\KeithPOSHLIB: ScriptDir is C:\Users\Keith\BinPARENT: After dot-sourcing libary ScriptDir is C:\Users\Keith\Bin
What happens here is that because we are “dot sourcing” PoshLib.ps1 into Parent.ps1, its definition of the $ScriptDir variable stomps the one created in Parent.ps1. Obviously this won’t do. You could try to create unique variable names for each script but that isn’t ideal and not very maintainable.
The best answer if you are on PowerShell 2.0 is to just use modules for your libraries. Modules can have “private” variables that don’t get exported so the variable “stomping” issue never arises. However, for various reasons, folks can’t always upgrade to PowerShell 2.0. So here is how you can fix this issue on PowerShell 1.0.
Essentially what you want to do is to dynamically evaluate the script’s location every time without having to use the longhand:
Split-Path $MyInvocation.MyCommand.Path –Parent
PowerShell allows us to create a shorthand for this using an anonymous scriptblock assigned to a variable like so:
Parent.ps1:$ScriptDir = { Split-Path $MyInvocation.ScriptName –Parent }"PARENT: Before dot-sourcing libary ScriptDir is $(&$ScriptDir)". "$(&$ScriptDir)\Bin\PoshLib.ps1""PARENT: After dot-sourcing libary ScriptDir is $(&$ScriptDir)"
PoshLib.ps1$ScriptDir = { Split-Path $MyInvocation.ScriptName –Parent }"POSHLIB: ScriptDir is $(&$ScriptDir)"
Note: that once we put the code within the scriptblock we need to switch from $MyInvocation.MyCommand.Path to $MyInvocation.ScriptName. These changes yield the desired results:
PS C:\Users\Keith> C:\Users\Keith\Parent.ps1PARENT: Before dot-sourcing libary ScriptDir is C:\Users\KeithPOSHLIB: ScriptDir is C:\Users\Keith\BinPARENT: After dot-sourcing libary ScriptDir is C:\Users\Keith
We all tend to customize our PowerShell environment via our Profile.ps1 script. Whether we load up snapins, modules, our own functions, etc. And with PowerShell 2.0, you can even “proxy” cmdlets such that you get to intercept calls to cmdlets like Get-Help and add parameters and augment its functionality. Sometimes though this can cause problems. And it isn’t always easy to determine if the problem is with PowerShell or one of these add-ins. When I run into these situations I fire up a PowerShell with no profile to help determine where the problem actually lies. This is what I execute:
C:\PS> PowerShell –noprofile
Often I prefer to have PowerShell fire up in a different window so I use Start-Process. And because I do this so often (testing PSCX), I’ve created a function that I put in my profile to provide a convenient shortcut to fire up PowerShell with no profile:
function psnp { Start-Process PowerShell.exe -Arg -noprofile }
The TFPT PowerShell snapin is a nice start but it is missing a key cmdlet IMO and that is Get-TfsPermission. Since TFS allows you to add individual users all over your directory structure, it can be quite hard rationalizing exactly who has access to what without traversing a bunch of directories in the Team Foundation client’s Source Control window, opening the Properties dialog and inspecting the Security settings. Fortunately there is also a command line way to do this via TFS’s tf.exe utility. Unfortunately the output of this command is just text and what’s worse is that this text is a pain to query over using PowerShell’s built-in querying cmdlets. So I wrote the following function to convert this text into objects which enables easier querying:
<#.SYNOPSIS Gets the Team Foundation server permissions for items based on the specified paths..DESCRIPTION Gets the Team Foundation server permissions for items based on the specified paths and outputs rich objects as opposed to text..PARAMETER Path The path to the Team Foundation server item..PARAMETER LiteralPath Specifies a path to one or more locations. Unlike Path, the value of LiteralPath is used exactly as it is typed. No characters are interpreted as wildcards. If the path includes escape characters, enclose it in single quotation marks. Single quotation marks tell Windows PowerShell not to interpret any characters as escape sequences..PARAMETER Recurse Gets permissions for the items at the specified location and in all child locations..EXAMPLE C:\PS> Get-TfsPermission C:\Tfs\Acme\Branches\Release\3.7 | Select ServerItem -exp Identities Get the permissions for the specified folder and expands the Identities array to show each identity. The server item corresponding to each identity object is tagged onto the object..EXAMPLE C:\PS> Get-TfsPermission C:\Tfs\Acme | Select ServerItem -expand Identities | Where {$_.Allow -match 'Checkin' } | Format-List ServerItem,Identity,Allow,Deny This command looks for items where the 'checkin' privilege has been granted locally..NOTES Author: Keith Hill Date: June 28, 2010 #>function Get-TfsPermission{ [CmdletBinding(DefaultParameterSetName="Path")] param( [Parameter(Mandatory=$true, Position=0, ParameterSetName="Path", ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, HelpMessage="Path to workspace item")] [ValidateNotNullOrEmpty()] [string[]] $Path, [Alias("PSPath")] [Parameter(Mandatory=$true, Position=0, ParameterSetName="LiteralPath", ValueFromPipelineByPropertyName=$true, HelpMessage="Path to workspace item")] [ValidateNotNullOrEmpty()] [string[]] $LiteralPath, [Parameter()] [switch] $Recurse )
Begin { Set-StrictMode -Version Latest $item = $null }
Process { if ($psCmdlet.ParameterSetName -eq "Path") { # In the -Path (non-literal) case we may need to resolve a wildcarded path $resolvedPaths = @($Path | Resolve-Path | Convert-Path) } else { # Must be -LiteralPath $resolvedPaths = @($LiteralPath | Convert-Path) } foreach ($rpath in $resolvedPaths) { Write-Verbose "Processing $rpath" $tfargs = '' $tfargs += if ($Recurse) { '/r' } # Look at each line of TF PERMISSION output and use a regex to determine # what the data for the line is. switch -regex (tf permission $rpath $tfargs) { '^Server item:\s+(\S+)\s+\(Inherit:\s+(\w+)\)' { # If previous object exists, output it before creating a new one if ($item) { $item } $item = new-object psobject -property @{ ServerItem = $matches[1] Inherits = $matches[2] -eq 'yes' Identities = @() } $item.psobject.TypeNames[0] = "TfsTools.VersionControl.ItemPermissions" } '\bIdentity:\s+(.*)$' { $identityName = $matches[1] $currentIdentity = new-object psobject -property @{ Identity = $identityName Allow = '' Deny = '' InheritedAllow = '' InheritedDeny = '' } $item.Identities += $currentIdentity } '\bAllow:\s*(.*)$' { $currentIdentity.Allow = $matches[1] } '\bDeny:\s*(.*)$' { $currentIdentity.Deny = $matches[1] } '\bAllow\s+\(Inherited\)\s*:\s*(.*)$' { $currentIdentity.InheritedAllow = $matches[1] } '\bDeny\s+\(Inherited\)\s*:\s*(.*)$' { $currentIdentity.InheritedDeny = $matches[1] } } } }
End { # Output the very last object if ($item) { $item } }}
The output of this function isn’t great for viewing since it is primarily intended to enable querying of the permission info on each server item. Note to the Team Foundation Power Tools devs, it sure would be nice to get this functionality into a future TFPT drop.
If you are dealing with an native executable that outputs UTF8 with no BOM (byte order marker) you will find that PowerShell garbles the input. This is most likely an issue with how the .NET console code interprets the incoming byte stream. Without a BOM it isn’t exactly easy to determine the proper encoding for a stream of bytes. For example take the following simple native exe source code that is supposed to output this (BTW ignore the fact that the text says ‘ASCII’ – it is really UTF8):
ASCII outputᾹ
Contents of stdout.cpp:
#include <stdio.h> void main() { char bytes[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x20, 0x6F, 0x75, 0x74, 0x70, 0x75, 0x74, 0xE1, 0xBE, 0xB9}; for (int i = 0; i < 15; i++) { printf("%c", bytes[i]); } }
#include <stdio.h>
void main()
{
char bytes[] = { 0x41, 0x53, 0x43, 0x49,
0x49, 0x20, 0x6F, 0x75,
0x74, 0x70, 0x75, 0x74,
0xE1, 0xBE, 0xB9};
for (int i = 0; i < 15; i++)
printf("%c", bytes[i]);
}
If you pipe the output of this program into a PSCX utility called Format-Hex (alias is fhex), you can see the actual unicode byte stream that was created by .NET’s interpretation of the incoming byte stream.
PS> .\stdout.exe | fhex Address: 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII -------- ----------------------------------------------- ---------------- 00000000 41 00 53 00 43 00 49 00 49 00 20 00 6F 00 75 00 A.S.C.I.I. .o.u. 00000010 74 00 70 00 75 00 74 00 DF 00 5B 25 63 25 t.p.u.t...[%c%
You might think you could pipe the output to Out-File –Encoding Utf8 but by the time the .NET strings hit the Out-File cmdlet the damage is already done. As can be seen if you view the subsequent output in Notepad.exe:
ASCII outputᾹ
The solution to this problem is to provide a hint to the .NET console functionality about the encoding of the incoming bytes. You can do this very simply:
PS> [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8
And it works:
6> .\stdout.exe | fhex Address: 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII -------- ----------------------------------------------- ---------------- 00000000 41 00 53 00 43 00 49 00 49 00 20 00 6F 00 75 00 A.S.C.I.I. .o.u. 00000010 74 00 70 00 75 00 74 00 B9 1F t.p.u.t... 7> .\stdout.exe | Out-File good.txt -Encoding UTF8
If you open good.txt in notepad you get:
And is as it should be. However this may seem somewhat counter-intuitive (it did to me) since the problem is with PowerShell/.NET “reading” console input and not writing it. Well one of the good folks on the PowerShell team pointed out to me that the Console OutputEncoding is probably inherited by child processes and a quick little experiment reveals this to be true. So by setting the OutputEncoding this determines how .NET encodes console output which helps PowerShell/.NET determine the correct encoding when reading in this information via console input.
One last point on this approach is that you should stash the original value of [Console]::OutputEncoding and restore it after you’ve run a problematic exe like in this example. I’ve found that the C# compiler will crash if you run it in PowerShell with the [Console]::OutputEncoding set to UTF8.
Some questions are coming up about the contents of PSCX 2.0. Here’s what in it:
CMDLETS:Add-PathVariableClear-MSMQueueConvertFrom-Base64ConvertTo-Base64ConvertTo-MacOs9LineEndingConvertTo-MetricConvertTo-UnixLineEndingConvertTo-WindowsLineEndingConvert-XmlDisconnect-TerminalSessionExpand-ArchiveExport-BitmapFormat-ByteFormat-HexFormat-XmlGet-ADObjectGet-AdoConnectionGet-AdoDataProviderGet-AlternateDataStreamGet-ClipboardGet-DhcpServerGet-DomainControllerGet-DriveInfoGet-EnvironmentBlockGet-FileTailGet-FileVersionInfoGet-ForegroundWindowGet-HashGet-HttpResourceGet-LoremIpsumGet-MountPointGet-MSMQueueGet-OpticalDriveInfoGet-PathVariableGet-PEHeaderGet-PrivilegeGet-PSSnapinHelpGet-ReparsePointGet-ShortPathGet-TabExpansionGet-TerminalSessionGet-TypeNameGet-UptimeImport-BitmapInvoke-AdoCommandInvoke-ApartmentJoin-StringNew-HardlinkNew-JunctionNew-MSMQueueNew-ShortcutNew-SymlinkOut-ClipboardPing-HostPop-EnvironmentBlockPush-EnvironmentBlockRead-ArchiveReceive-MSMQueueRemove-AlternateDataStreamRemove-MountPointRemove-ReparsePointResolve-HostSend-MSMQueueSend-SmtpMailSet-BitmapSizeSet-ClipboardSet-FileTimeSet-ForegroundWindowSet-PathVariableSet-PrivilegeSet-VolumeLabelSkip-ObjectSplit-StringStart-TabExpansionStop-TerminalSessionTest-AlternateDataStreamTest-AssemblyTest-MSMQueueTest-ScriptTest-UserGroupMembershipTest-XmlUnblock-FileWrite-BZip2Write-ClipboardWrite-GZipWrite-TarWrite-Zip
FUNCTIONS:Add-DirectoryLengthAdd-ShortPathDismount-VHDEdit-FileEdit-HostProfileEdit-ProfileEnable-OpenPowerShellHereGet-ChildItemGet-HelpGet-PropertyValueGet-ScreenCssGet-ScreenHtmlGet-ViewDefinitionhelpInvoke-BatchFileInvoke-ElevatedInvoke-GCInvoke-MethodInvoke-NullCoalescingInvoke-ReflectorInvoke-TernarylessMount-VHDNew-HashObjectOut-SpeechpromptQuoteListQuoteStringResolve-ErrorRecordResolve-HResultResolve-WindowsErrorSearch-TranscriptSet-LocationExSet-ReadOnlySet-WritableShow-TreeStop-RemoteProcess
ALIASES:?:??callcvxmleehpepfhexfxmlgcbgpvgtnigclnloremnhoocbqlqsResize-Bitmaprfrverrvhrrvwerskipslssrosrtssuswrtailtouch
PROVIDERS:AssemblyCacheDirectoryServicesPscxSettingsFeedStore
One issue with PowerShell providers is that only the Filesystem and Registry providers’ hierarchies are easily viewable using Windows Explorer and Regedit respectively. The other PowerShell providers are mostly without such a view. This is problematic for providers that supply configuration information like the WSMan provider. Without a lot of cd’ing around it is hard to get a “lay of the land” with respect to the various settings that are available.
In Pscx 2.0, we set out to help with this problem by providing the equivalent of the old DOS tree.com routine. The command is called Show-Tree. If you have the final PSCX 2.0 bits installed try this from an elevated prompt:
C:\PS> Show-Tree wsman: -ShowLeafWSMan:\└──localhost ├──MaxEnvelopeSizekb ├──MaxTimeoutms ├──MaxBatchItems ├──MaxProviderRequests ├──Client │ ├──NetworkDelayms │ ├──URLPrefix │ ├──AllowUnencrypted │ ├──Auth │ │ ├──Basic │ │ ├──Digest │ │ ├──Kerberos │ │ ├──Negotiate │ │ ├──Certificate │ │ └──CredSSP │ ├──DefaultPorts │ │ ├──HTTP │ │ └──HTTPS │ └──TrustedHosts ├──Service │ ├──RootSDDL │ ├──MaxConcurrentOperations │ ├──MaxConcurrentOperationsPerUser │ ├──EnumerationTimeoutms │ ├──MaxConnections │ ├──MaxPacketRetrievalTimeSeconds │ ├──AllowUnencrypted │ ├──Auth │ │ ├──Basic │ │ ├──Kerberos │ │ ├──Negotiate │ │ ├──Certificate │ │ ├──CredSSP │ │ └──CbtHardeningLevel │ ├──DefaultPorts │ │ ├──HTTP │ │ └──HTTPS │ ├──IPv4Filter │ ├──IPv6Filter │ ├──EnableCompatibilityHttpListener │ ├──EnableCompatibilityHttpsListener │ └──CertificateThumbprint ├──Shell │ ├──AllowRemoteShellAccess │ ├──IdleTimeout │ ├──MaxConcurrentUsers │ ├──MaxShellRunTime │ ├──MaxProcessesPerShell │ ├──MaxMemoryPerShellMB │ └──MaxShellsPerUser ├──Listener │ └──Listener_641507880 │ ├──Address │ ├──Transport │ ├──Port │ ├──Hostname │ ├──Enabled │ ├──URLPrefix │ ├──CertificateThumbprint │ ├──ListeningOn_1770022257 │ ├──ListeningOn_1480808289 │ ├──ListeningOn_4173834 │ ├──ListeningOn_228071226 │ ├──ListeningOn_1414502903 │ ├──ListeningOn_222657401 │ ├──ListeningOn_616175923 │ ├──ListeningOn_783876958 │ ├──ListeningOn_704030885 │ ├──ListeningOn_405255605 │ └──ListeningOn_1490189075 ├──Plugin │ ├──Event Forwarding Plugin │ │ ├──xmlns │ │ ├──Name │ │ ├──Filename │ │ ├──SDKVersion │ │ ├──XmlRenderingType │ │ ├──lang │ │ ├──InitializationParameters │ │ └──Resources │ │ └──Resource_1958972577 │ │ ├──ResourceUri │ │ ├──SupportsOptions │ │ ├──ExactMatch │ │ ├──Capability │ │ └──Security │ │ └──Security_371857150 │ │ ├──Uri │ │ ├──ExactMatch │ │ ├──Sddl │ │ └──xmlns ...
You can use this on most providers although some providers behave better than others under this command. Here’s a way to view registry entries e.g.:
C:\PS> Show-Tree HKLM:\SOFTWARE\Microsoft\.NETFramework -Depth 2 -ShowPropertyHKLM:\SOFTWARE\Microsoft\.NETFramework├──Property: COMPLUS_HACK_DisableSideBySide = 1├──Property: DbgJITDebugLaunchSetting = 16├──Property: DbgManagedDebugger = "C:\Windows\system32\vsjitdebugger.exe" PID %d APPDOM %d EXTEXT "%s" EVTHDL %d├──Property: Enable64Bit = 1├──Property: InstallRoot = C:\Windows\Microsoft.NET\Framework64\├──AssemblyFolders│ ├──Property: SubKeyCount = 10│ ├──Property: ValueCount = 0│ ├──ADOMD.Client 10.0│ │ └──Property: (default) = C:\Program Files\Microsoft.NET\ADOMD.NET\100\│ ├──Microsoft .NET Framework 3.5 Reference Assemblies│ │ └──Property: (default) = C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\│ ├──MSDeploy│ │ └──Property: (default) = C:\Program Files\IIS\Microsoft Web Deploy\│ ├──SQL Server Assemblies│ │ └──Property: (default) = C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\│ ├──SSIS Connection Managers 100│ │ └──Property: (default) =│ ├──SSIS ForEach Enumerators 100│ │ └──Property: (default) = C:\Program Files\Microsoft SQL Server\100\DTS\ForEachEnumerators│ ├──SSIS Pipeline Components 100│ │ └──Property: (default) = C:\Program Files\Microsoft SQL Server\100\DTS\PipelineComponents\│ ├──SSIS Tasks 100│ │ └──Property: (default) = C:\Program Files\Microsoft SQL Server\100\DTS\Tasks│ ├──v3.0│ │ ├──Property: <IncludeDotNet2Assemblies> = 1│ │ └──Property: All Assemblies In = C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\│ └──v3.5│ ├──Property: <IncludeDotNet2Assemblies> = 1│ └──Property: All Assemblies In = C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\├──NGenQueue│ ├──Property: SubKeyCount = 2│ ├──Property: ValueCount = 0│ ├──WIN32│ │ ├──Property: SubKeyCount = 2│ │ └──Property: ValueCount = 0│ └──WIN64│ ├──Property: SubKeyCount = 2│ └──Property: ValueCount = 0...
Give PSCX 2.0 a try and let us know what you think - good or bad!
Finally! We’ve gotten PSCX 2.0 finished and released. A few notes about this release. It targets only Windows PowerShell 2.0 as it is module based. Also, the deployment of the PSCX 2.0 is xcopy. There is no installer. For more more info, check out the releases notes on the PSCX 2.0 download page. Please let us know if you find any issues. NOTE: Be sure to Unblock the ZIP before extracting it to your Modules folder.
One of the big benefits of Windows 2008 R2 is the fact that PowerShell v2 is installed by default and that AD can be administered by PowerShell. There are 76 AD cmdlets and an AD provider. We’ll start by looking at the cmdlets. Organizational Units are the subdivisions with a domain. We can easily create a new OU. New-ADOrganizationalUnit -Name "AllUsers" -ProtectedFromAccidentalDeletion $true The default location is to create OUs in the root of the domain. I really like the ability to...( read
From what I can see, CTP3's scoped execution policy settings can be used to cause a change in PowerShell behavior that can't be reversed intuitively. See this suggestion on Microsoft Connect: Possible need for Clear-ExecutionPolicy https://connect.microsoft.com/feedba...9788&SiteID=99 Basically, PowerShell sessions inherit machine-scoped execution policies until you define an execution policy with user scope. Once you've created a user-scoped execution policy, you can modify it, but
Updated to PowerBoots 0.1 An introduction to PowerBoots Please excuse me if I start by just copying the basic ideas of the Shoes Tutorial , but I figured that since PowerBoots is inspired by Shoes, that was as good a place as any to start. PowerBoots (or just “Boots”) is a PowerShell 2.0 module with functions for writing Windows Presentation Framework ( WPF ) applications in the PowerShell scripting language. You should get the latest version of PowerBoots before continuing, and ins
Andrew Kutz just posted a new Hyper9 Cmdlet on sourceforce.net called Out-DataSet. Out-DataSet is an extremely useful and needed cmdlet. You can use it to pipe any type of data that can be formatted with the built-in cmdlet, Format-Table, into a typed Microsoft .NET System.Data.DataSet. This allows an unlimited manipulation of data that you simply cannot achieve with text globbing. This cmdlet is experimental in nature because it was achieved by reverse engineering the Format-Table cmdlet
• Updates : 3/28/2009 and 3/29/2009 Note: This post is updated daily or more frequently, depending on the availability of new articles. Entity Framework and Entity Data Model (EF/EDM) • Julie Lerman takes on many-to-many associations again in her Inserting Many to Many Relationships in EF with or without a Join Entity post of 3/29/2009. Binary Bob ’s Disconnected Clients, Changed data and Entity Framework post of 3/25/2009 discusses the use of EF with Silverlight apps. B
Ya hemos comentado en articulos anteriores sobre Windows Powershell lo que son los commandlets. Hoy vamos a ver uno de ellos que nos facilitara en un momento dado el trabajo. 4.1- Get-command Este cmdlet obtiene informacion basica acerca de los cmdlets y otros elementos de comandos de Windows Powershell como archivos, funciones y proveedores de Windows Powershell. Parametros: -name Obtiene...
While I finish up another blog solution, this time on importing a table from Word into Excel, I thought I would share some information on two useful tools you guys can leverage when building Open XML solutions. The first tool I want to talk about is the next release of PowerTools for Open XML. PowerTools for Open XML is an open source project on CodePlex , which is entirely based on version 1 of the Open XML SDK. This tool supports the PowerShell piping architecture, by providing 30+ cmdlets.
This is a bit of a hardcore example of the power that modules have brought to the table in v2.0. First, a little background - PowerShell’s type adaptation system has always ignored the concept of interfaces. Frankly, it never really needed to pay them any attention. The adapted view of any instance of a .NET class is just an aggregate of all its methods; the most derived instance is the default, and only, view. This is the most simple and most frequently used view. However, this all goes to hell
————————————————————————————————————— ————————————————————————————————————— Veeam Software , award-winning provider of systems management tools for VMware virtual datacenter environments, today delivers new executive management reporting, integration with corporate CMDBs (change management databases), new change management capabilities, and numerous technical enhancements in version 3.5 of its Veeam Reporter and Veeam Reporter Enterprise products. Veeam Reporter is a favorite of virtual
If you’re getting your Powershell on ( and frankly you should ), Microsoft have released a ~440 page compendium ( in *.docx format ) of Cmdlets you can use under the SCVMM Powershell. Now just remember the easiest way to get thru the document is view the Document Map . ( click View and then select the Document Map check box ) The reference is a prettied up and portable version of: Get-Command -PSSnapin Microsoft.SystemCenter.VirtualMachineManager | Sort?Object Noun, Verb | Get-H
Silverlight / WPF Boss Launch Beta (Andy Beaulieu) Silverlight RPG: Steel Saga (Avi Pilosof) Silverlight 3 Beta - Downloads You May Have Missed (Bart Czernicki) Compile your XNA 2D games to run in Silverlight with SilverSprite (Bill Reiss) Silverlight, XAML & Prism: Diving in Head First at the Shallow End of the Pool (Bobby Johnson) Creating Sound Using MediaStreamSource in Silverlight 3 Beta and Using Blur to make Dialogs Pop in Silverlight 3 (Pete Brown)
A Podcast about Windows PowerShell. Listen: In This Episode Tonight on the PowerScripting Podcast we talk to Brandon Shell about Active Directory support in PowerShell V2 News This segment is brought to you by Idera: Want to make Windows PowerShell easier than ever to learn and master? Checkout Idera’s PowerShellPlus Professional Edition which is now available for download! The new version has vastly improved code completion and a slick interactive Learning Cen
Windows Azure, Azure Data Services, SQL Data Services and related cloud computing topics now appear in this weekly series. Note: This post is updated daily or more frequently, depending on the availability of new articles. • • • Updated 3/20/2009 and 3/21/2009: Additions •• Updated 3/19/2009 10:00 AM PDT: Addition of more MIX 09-based content • Updated 3/18/2009 11:00 AM PDT: Additions: Windows Azure Tools and SDK March CTP update, ADO.NET Data Services 1.5 March CTP
There’s a bit of a flap going on now around the blogosphere as various vendors, eager to get onto the PowerShell train, are doing the bare minimum to get their product “powershellized.” No one has spent any time reading – or if they did, they weren’t successful in trying to understand it – the Microsoft Command Line Standard . Modules as Namespaces That’s right, modules are not as crazy sounding as they seem. They can be used quite simply to just group a load of function
GoGrid continues to be a pioneering force in Cloud Computing Infrastructure and Hosting segments with a variety of initiatives, making it a clear choice for Windows Server 2008, 2003, SQL Server and .NET developers. San Francisco, CA ( Vocus / PRWEB ) March 17, 2009 — GoGrid , the Cloud Computing division of ServePath , LLC delivers a solid implementation of programs, services and features optimized for Mi
After being a part of a few email threads and discussions about Citrix not following the traditional name-verb cmdlet naming scheme I thought I would make a post with my take on that. Basically, Citrix joined companies like Microsoft, VMware and Quest and is also trying to PowerShell-enable all of their products. However, they chose to not follow the standard naming scheme and went with a few of their own. The ones for Provisioning Server are just bad: They user verb instead of a noun, p
Windows PowerShell で SQL Server 2008 を管理する方法を調べてみました。 今回使用した SQL Server は SQL Server 2008 Express になります。 最初、SQL Server 2008 Express では Windows PowerShell が使用できないかと思っていたのですが、Windows PowerShell 1.0 を先にインストールしておけば、SQL Server 2008 Express でも Windows PowerShell で SQL Server を操作できるようになります。 SQL Server PowerShell is an option component in SQL Express 2008. Optional in this case means that we do not make Windows PowerShell 1.0 a prerequisite for SQL Express 2008, but it is required to use the new
Windows Azure, Azure Data Services, SQL Data Services and related cloud computing topics now appear in this weekly series. Note: This post is updated daily or more frequently, depending on the availability of new articles. ••• Updated 3/12-15/2009: More Additions (Amazon EC2 and Rackspace Cloud Servers price war, more S[S]DS updates) • • Updated 3/11/2009: Additions (mostly about the SDS course reversal) • Updated 3/10/2009 to add SQL Data Services Abandons REST
My job as an evangelist focuses on Windows platform (client and Server OS), including management (i.e. PowerShell) and Virtualization. But there are other Microsoft products which from my day to day use of them I feel evangelical about. One is Windows Live Writer which is the best tool for composing blog posts that I’ve found. Word can do blog posts, but somehow writer feels better suited to the task. I used to do a lot with office Communications server (and I’ve written sections for both of
Matt Uyttendaele , from Microsoft Research, contacted me about using PowerShell to transform files for the Microsoft ICE product. Microsoft Image Composite Editor is an advanced panoramic image stitcher which creates multi-resolution tiled formats like HD View and Silverlight Deep Zoom . We went from this To This Matt wanted to convert to Xml, laying out nodes for the file name, X and Y translation and in the process apply the scaling factor from the matrix to each coord
I'm writing this post from version 12.0.1366.1026 of Windows Live Writer - updated as part of Windows Live Wave 2 of the Windows Live suite of applications, which was released overnight. That includes a new version of Live Messenger, Live Mail, Live Photo Gallery etc.
Also released this week was the first alpha of Paint.NET 3.20. I don't know many people who still don't use Paint.NET, but it's an invaluable addition to Windows if you do any kind of image manipulation. See Rick's blog post for the full list of new features and changes in this version.
And lastly we have the first CTP of PowerShell 2.0! Remember that this is a CTP drop, so it's not for installation on production machines. See Jeffrey's post about CTPs if you're not sure.
In my last post I described a way to list out all your subscribed feeds that haven't been posted to in four months. This works well, but leaves it up to you to do something with those feeds.
The next logical step is to let PowerShell worry about them. That is, have the script itself automatically delete the feeds, or perhaps move them to a "Dead" folder for you to keep an eye on.
Now, the current version the PowerShell Community Extensions does not have a FeedStoreProvider that supports the "move-item" command, but that'll change soon (I've already written the code). In the meantime, here's how you'd delete the old feeds:
cd feed:\ gci -rec | ?{ $_.Type -eq "Feed" } | %{ $_.Items | sort -desc PubDate | select -first 1 } | ?{ $_.PubDate -lt [DateTime]::Now.AddMonths(-4) -and $_.PubDate.Year -gt 1899} | %{ $_.Parent } | remove-item -recurse
Note the "-recurse" parameter on remove-item. That's because feeds are technically containers (they contain feed items), so unless you specify the "-recurse" parameter you will be prompted to delete each feed.
Next, here's how you'd move the old feeds to a "Dead" folder:
cd feed:\ new-item -itemType Folder Dead gci -rec | ?{ $_.Type -eq "Feed" } | %{ $_.Items | sort -desc PubDate | select -first 1 } | ?{ $_.PubDate -lt [DateTime]::Now.AddMonths(-4) -and $_.PubDate.Year -gt 1899} | %{ $_.Parent } | move-item -destination \Dead
One small caveat: You have to be sitting in the root of your "feed:" drive for this to work. The reason for this is that remove-item and move-item both look for a "Path" property on the item that gets piped through to them, and for feeds the "Path" property is a relative path from the root folder (eg. "Blogs\Mabsterama"). So the commands won't find the item to remove or move unless you're currently sitting in "feed:\".
With the new year well and truly underway, it's time to go over all those RSS feeds you're subscribed to, and clean out the ones that don't get updated any more.
But how? How could you possibly know which feeds haven't been posted to in the past, say, four months?
Why - with Windows PowerShell, of course! With a little help from the PowerShell Community Extensions and its awesome Common Feed Store provider!
So here's the command:
gci feed:\ -rec | ?{ $_.Type -eq "Feed" } | %{ $_.Items | sort -desc PubDate | select -first 1 } | ?{ $_.PubDate -lt [DateTime]::Now.AddMonths(-4)} | select @{Name="Feed" Expression={$_.Parent.Name}}, PubDate | ft -au
... and here's the result on my home PC:
Feed PubDate ---- ------- DataWorks WebLog 12/07/2006 7:57:00 AM Smart Client Data 9/08/2006 8:10:00 PM What's in Store 27/06/2006 1:26:00 PM Mick's Mix 21/08/2006 9:07:00 PM Microsoft at Home 20/06/2006 6:00:00 PM Microsoft at Work 9/05/2006 6:00:00 PM Xbox World Australia 30/12/1899 12:00:00 AM DVD Plaza 30/12/1899 12:00:00 AM Luke Hutteman 9/01/2006 5:12:37 AM Microsoft at Home 20/06/2006 6:00:00 PM Microsoft at Work 9/05/2006 6:00:00 PM
Notice that a couple of those feeds have a date in 1899. That means that they don't provide a date against their posts, so they can be ignored - might as well keep hold of 'em. The rest can go! Ah ... a fresh start. :)
Here's a little one-liner to list out your all your Internet Explorer favorites:
gci $env:userprofile\favorites -rec -inc *.url | ? {select-string -inp $_ -quiet "^URL=http"} | select @{Name="Name"; Expression={[IO.Path]::GetFileNameWithoutExtension($_.FullName)}}, @{Name="URL"; Expression={get-content $_ | ? {$_ -match "^URL=http"} | % {$_.Substring(4)}}}
Keith Hill has just announced the official 1.0 release of the PowerShell Community Extensions. This is a collection of handy cmdlets and functions for Windows PowerShell, and includes my provider for the Windows RSS Platform common feed store (which gives you a "feed:" drive on Vista, or if you have IE7 or Outlook 2007 installed).
Go grab it!
The official "RTW" (release to web) version of Windows PowerShell is now available! Check out the Windows PowerShell Team Blog for more details and download links.
This release is actually identical to the RC2 release, except with a new installer and licence agreement. The guys obviously did such a good job with RC2 that nothing needed to change. My FeedStoreProvider still works just fine without being recompiled or anything.
Well done, guys!
Another month and another AWDNUG meeting, and if you missed this one you should be kicking yourself.
Michael from Border Express gave an excellent presentation about SQL Server replication, breaking down the different types of replication, the pros and cons of each type, and how (and what) to monitor them. He also touched on some query optimization tips which I think were appreciated by everyone.
Did you know that if you select a table name in Sql Server Management Studio and hit Alt+F1 it dumps the table structure to the query results? I didn't either.
We had about 20 minutes left over after Michael's presentation (did I mention it was excellent?), so I gave a quick introduction to Windows PowerShell. There really wasn't enough time to touch on all the things that PowerShell is capable of, and I didn't have anything prepared, but I hope that those present got an idea of just how amazing this little command-line engine is.
The turn-out this month was less than spectacular, but I'm sure that all those who didn't attend will read this post and realised that they can't afford to miss any more meetings! See you in December, guys!
Keith and co over at the PowerShell Community Extensions project has released an alpha version, which gives you some neat new commands like "get-clipboard" and "set-filedate" in PowerShell.
The next version will include my Common Feed Store provider!
So anyway, head on over to CodePlex and download the first alpha - the guys have done a great job with the install and the cmdlets so far are worthwhile.
I'm going nuts coming up with new and exciting command-lines to interrogate the common feed store using my new PowerShell provider. Check this one out:
cd feed: dir -rec | where {$_.Author.Length -gt 0} | group Author | sort Count -desc | select -first 20 Name, Count | ft -au
That'll get you the top 20 authors across your entire feed list. Note that this is not looking for the feeds with the most items - it's the authors, so if one author posts to more than one feed then he's counted in both places. That said, in my feeds at work Robert Scoble is winning by a mile:
Name Count ---- ----- Robert Scoble 156 Zonk 63 mabster 60 Michael S. Kaplan 53 daveburke 52 Jason Haley 44 Cyrus Farivar 44 kdawson 42 crucible 42 Mitch Denny 40 Leon Bambrick 36 Paul Miller 33 bsimser 32 GregLow 31 ieblog 31 oldnewthing 31 Long Zheng 31 Darren Murph 30 Carl Franklin 29 Daniel Moth 28
The latest stuff I've been playing with in my PowerShell FeedStoreProvider is the ability to create and delete items within the store.
So now you can do this:
# create a new folder called "Mad Props" new-item -name "Mad Props" -type folder # create a new feed withing that folder cd "Mad Props" new-item -name Mabsterama -type feed -value http://www.madprops.org/cs/blogs/mabster/rss.aspx
Similarly, "remove-item" works too. You can delete a feed or folder like this:
remove-item Mabsterama
Next thing I want to get working is support for the "-whatif" parameter. In PowerShell you can pass "-whatif" to any command that might do something dangerous (like remove-item) and it'll inform you of what it would have done without actually doing anything. Nice.
Finally worked the last annoying bug out of my FeedStoreProvider for Windows PowerShell yesterday. If you're interested, I was passing an integer as the first parameter to WriteItemObject() from within my GetChildNames override, and it was causing grief. Always pass strings from there, people!
Anyway, here's my latest trick. Want some stats about your feed subscriptions?
cd feed: gci -recurse | where {$_ -is [Microsoft.Feeds.Interop.IFeed]} | Measure-Object ItemCount -sum -av -min -ma
So we're getting all the child items on my "feed:" drive recursively, filtering the list so that we're only talking about feeds and not folders or feed-items, then getting some statistics from the list. The result on my home box:
Count : 87 Average : 93.551724137931 Sum : 8139 Maximum : 200 Minimum : 2 Property : ItemCount
So, I'm subscribed to 87 feeds here, with a total of 8139 items. On average, each feed has about 93 items.
Incidentally, I've added a custom parameter to "get-childitem" called "-unread", so you can, if you desire, list out only unread stuff in your subscriptions. That is, folders that contain feeds with unread items, feeds with unread items, and unread feed items. Very handy!
After my success yesterday in accessing the Common Feed Store from PowerShell, I started to wonder just how difficult it would be to write a provider for PowerShell so that I could access my feeds as if they were folders and files. You see, in PowerShell, you're not limited to hard drives and files when you use "dir", "cd" etc - you can change into the Windows Registry, or into the certificate store ... all sorts of places.
So I did some reading on MSDN about creating your own PowerShell providers.
A few hours later, and I can now do this:
cd feed: dir -recurse | where {$_.Modified -gt [DateTime]::Now.AddMinutes(-90)} | ft Title
... which yields this output:
Title ----- Opening & Saving Word, PowerPoint and Excel 2... Niche Player No More; Apple in the running f... Where's the Cowboy Talk Now? Open Source Music Software & The AGNULA Proje... Zelda:Twilight Princess for GameCube NOT to b... Old buddies reunite in hopes of taking tech w... Fifteen Exercises for Learning a new Programm... LOST - Further Instructions Recap Engadget's relaunch giveaways: Slingbox PRO w...
In other words, I can list out from the command-line all the items in my feeds that have been updated in the last 90 minutes!
I'll keep working on this provider to give it more functionality. For example, right now you can't use wildcards when you type "dir", and there is no default formatting for feeds, so you need to pipe everything through to format-table to get a nice output. Still, not bad for a few hours' work!!!
Oh, this is just too much fun. Check this out - a PowerShell script to display the feeds you're subscribed to (in the common RSS store you get with IE7) which have unread items:
$feeds = (new-object -ComObject Microsoft.FeedsManager); % { $feeds.RootFolder.Subfolders | % {$_.Feeds} | select-object -pr UnreadItemCount, Title; $feeds.RootFolder.Feeds | select-object -pr UnreadItemCount, Title } | where {$_.UnreadItemCount -gt 0} | format-table -au
I haven't found a "nice" way to combine the output of two commands into one array, but just executing both together inside a scriptblock like this appears to work.
What do you think? It's amazing the sort of stuff you can do in PowerShell!
Update! You can combine the results of two commands into a single array! Here's an updated script:
$feeds = (new-object -ComObject Microsoft.FeedsManager); @( $feeds.RootFolder.Subfolders | % {$_.Feeds} | select-object -pr UnreadItemCount, Title; $feeds.RootFolder.Feeds | select-object -pr UnreadItemCount, Title ) | where {$_.UnreadItemCount -gt 0} | format-table -au
And here's the output on my machine right now:
UnreadItemCount Title --------------- ----- 4 digg 1 Slashdot
For the past few days I've been messing around with Windows PowerShell. PowerShell is the next-generation command-line environment for Windows and associated server products (for example, Exchange 2007 will use it as a command-line and scripting interface). It's way beyond anything you've used in a command-line in the past, because instead of passing text from command to command (via a pipeline) you're passing fully-fledged .NET objects.
Let me give you an example!
Let's say you want to know some statistics about lines of code in your source tree. The first thing you need to get is a list of C# files on your drive. No problem:
get-childitem \ -recurse -include *.cs
What are we doing here? Well, first we're using the get-childitem to recursively list every file on our hard drive with an extension of ".cs". The output of get-childitem is not a simple string of text - it's an array of System.IO.FileInfo classes.
So now we need information about the contents of each of those files. How many lines are in each file? Let's pipe our output through to another command:
dir \ -recurse -include *.cs | get-content
The get-content command returns the contents of a file as an array of strings, one for each line. We want to get the length of that array (the number of lines in the file), so let's use that get-content call in a slightly different way:
dir \ -recurse -include *.cs | select-object @{Name="Lines"; Expression={(get-content $_).Length}}
The select-object command selects certain properties from the given object (in this case the FileInfo objects in the array returned by "get-childitem"). We're returning a calculated property called "Lines" which is the number of lines in the file (based on the length of the array returned by get-content).
So now our command is returning an array of Int32 values - the number of lines in every C# file on our hard drive. Last thing we want to do is get some statistics about those numbers:
dir \ -recurse -include *.cs | select-object @{Name="Lines"; Expression={(get-content $_).Length}} | measure-object Lines -av -sum -max -min
And we're done! The measure-object command gives us all the statistics we need. Here's an example of the output from the above string of commands:
Count : 63 Average : 221.587301587302 Sum : 13960 Maximum : 8425 Minimum : 20 Property : Lines
Pretty cool, huh? One line of commands to get a bunch of handy statistics about your lines-of-code.
This is a really simple breakdown of what's possible in PowerShell. Check out the team blog for a more in-depth look at the amazing things this thing is capable of!
Last edited Dec 7, 2006 at 10:16 PM by codeplexadmin, version 1
Ads by Developer Media