PowerShell command to parameterised PowerShell function? - powershell-2.0

I'm not so hot with PowerShell yet, but have managed to get this command to work quite nicely:
get-childitem "C:\Code\WC1" -Recurse | select-string "insert into\s+my_table"
Thing is, I know I'm going to struggle to remember this, so how can I make it into a function where the path supplied to get-childitem and the search regex are parameters?
I'm using PowerShell 2.0.

more commonly these days the parameters are being called after the function declaration e.g.
Function Find-Code {
param([string] $path, [string] $pattern)
get-childitem $path -Recurse | select-string $pattern
}

Function Find-Code([string] $path, [string] $pattern)
{
get-childitem $path -Recurse | select-string $pattern
}
You can put this in your PowerShell Profile.
An easy way to do this is to edit the $profile file (run something like notepad $profile from your PowerShell prompt) and just paste the text right in.

Related

Search in/for text in files

I am currently learning LPTHW Ex 46. In his video tutorial, Zed had done the following commands:
Find NAME within files using grep -r "NAME" *.
Find all files with extension ending in .pyc using find . -name "*pyc" -print.
Unfortunately, the above code does not work on Windows PowerShell. May I know what their Windows PowerShell equivalents are?
Based on my search, item 1 can be replaced by Select-String. However, it is not as good as we can only search specific files and not directories. For example, while this would work:
Select-String C:\Users\KMF\Exercises\Projects\gesso\gesso\acrylic.py -pattern "NAME"
this would not:
Select-String C:\Users\KMF\Exercises\Projects\gesso -Pattern "NAME"
and it gives the following error
Select-String : The file C:\Users\KMF\Exercises\Projects\gesso can not be read: Access to the path 'C:\Users\KMF\Exercises\Projects\gesso' is denied.
For item 2 I could not find a similar function.
grep and find are Unix/Linux shell commands. They won't work in PowerShell unless you install a Windows port of them.
As you already found out, Select-String is the PowerShell equivalent for grep. It doesn't recurse by itself, though, so you have to combine it with Get-ChildItem to emulate grep -r:
Get-ChildItem -Recurse | Select-String -Pattern 'NAME'
For emulating find you'd combine Get-ChildItem with a Where-Object filter:
Get-ChildItem -Recurse | Where-Object { $_.Extension -eq '.pyc' }
PowerShell cmdlets can be aliased to help administrators avoid extensive typing (since PowerShell statements tend to be rather verbose). There are several built-in aliases, e.g. ls or dir for Get-ChildItem, and ? or where for Where-Object. You can also define aliases of your own, e.g. New-Alias -Name grep -Value Select-String. Parameter names can be shortened as long as the truncated parameter name remains unique for the cmdlet. When cmdlets allow positional parameters they can even be omitted entirely.
With all of the above your two PowerShell statements can be reduced to the following:
ls -r | grep 'NAME'
ls -r | ? { $_.Extension -eq '.pyc' }
Note however, that aliases and abbreviations are mainly intended as an enhancement for console use. For PowerShell scripts you should always use the full form, not only for readability, but also because aliases may differ from environment to environment. You don't want your scripts to break just because they're run by someone else.

powershell v2, use cmdlet with parameters known only at truntime

I need to use cmdlet but will only know what parameter to use at runtime.
for ex, I may need to call
Get-ChildItem -filter <string>
or
Get-childItem -exclude <string>
or
Get-childItem -recurse
etc.
but I will know only at runtime which of -filter or -exclude or -recurse or a combination of them be needed.
[ Get-ChildItem is only an example to illustrate the need not the real thing ]
is it possible to do this except by using a huge if-then-else structure ? is it possible to use a cmdlet selecting parameters at runtime ?
TIA, Peyre.
using you example you can do it something like this:
Function test ($value , [switch]$paramA, [switch]$paramB)
{
if ($paramA)
{
$cmd = "-filter $value"
}
if($paramB)
{
$cmd = "-exclude $value"
}
iex "dir $cmd"
}
to test it try it like this:
test -value *.ps1 -paramA
or
test -value *.ps1 -paramB

Powershell wildcard on returns items

I'm trying to determine whether the directories under a specified root directory contain files that match a certain pattern, in my case RT*.dcm.
I'm using Powershell 2.0 and I first obtain all sub-directories beneath the specified root directory using
$dirList = Get-ChildItem $homeDir -recurse | where {$_.Attributes -eq 'Directory'} | Select-Object FullName
I then loop through these to see if they contain *.dcm files using (perhaps there's a better way?)
# Find files with a "dcm" extension.
$fileList = Get-ChildItem $dir.fullname | where {$_.extension -eq ".dcm"} | Select-Object FullName
# Look for directories that contain *.dcm files
if ($fileList.Count -gt 0) {
[Console]::WriteLine("Dicom directory: " + $dir.fullname)
$dicomDirList += $dir
}
The above two sections work ok
I then search through the found directories using
foreach($dir in $dicomDirList) {
$rtFileList = Get-ChildItem $dir | where {$_.name -like "RT*.dcm"} | Select-Object FullName
foreach($file in $rtFileList) {
[Console]::WriteLine("RT likey file: " + $file.fullname)
}
}
However this doesn't find the files I know that are there?
If I use
Get-ChildItem C:\myfolder\RT*.dcm
this works, but I can't figure out how to use the returned items from the previous Get-ChildItem call
Could someone please point me in the right direction?
It looks like you may be over-complicating things.
To accomplish what Get-ChildItem C:\myfolder\RT*.dcm does for the entirety of $homeDir (which is what I believe you're trying to do), you can use a single Get-ChildItem command:
Get-ChildItem $homeDir -Recurse | Where-Object{$_.Name -like "RT*.dcm"}
This searches the entirety of $homeDir recursively for all of the .dcm files you're looking for and returns them.

Powershell: Count items in a folder with PowerShell

I'm trying to write a very simple PowerShell script to give me the total number of items (both files and folders) in a given folder (c:\MyFolder). Here's what I've done:
Write-Host ( Get-ChildItem c:\MyFolder ).Count;
The problem is, that if I have 1 or 0 items, the command does not work---it returns nothing.
Any ideas?
You should use Measure-Object to count things. In this case it would look like:
Write-Host ( Get-ChildItem c:\MyFolder | Measure-Object ).Count;
or if that's too long
Write-Host ( dir c:\MyFolder | measure).Count;
and in PowerShell 4.0 use the measure alias instead of mo
Write-Host (dir c:\MyFolder | measure).Count;
I finally found this link:
https://blogs.perficient.com/microsoft/2011/06/powershell-count-property-returns-nothing/
Well, it turns out that this is a quirk caused precisely because there
was only one file in the directory. Some searching revealed that in
this case, PowerShell returns a scalar object instead of an array.
This object doesn’t have a count property, so there isn’t anything to
retrieve.
The solution -- force PowerShell to return an array with the # symbol:
Write-Host #( Get-ChildItem c:\MyFolder ).Count;
If you need to speed up the process (for example counting 30k or more files) then I would go with something like this..
$filepath = "c:\MyFolder"
$filetype = "*.txt"
$file_count = [System.IO.Directory]::GetFiles("$filepath", "$filetype").Count
Only Files
Get-ChildItem D:\ -Recurse -File | Measure-Object | %{$_.Count}
Only Folders
Get-ChildItem D:\ -Recurse -Directory | Measure-Object | %{$_.Count}
Both
Get-ChildItem D:\ -Recurse | Measure-Object | %{$_.Count}
You can also use an alias
(ls).Count
Recursively count files in directories in PowerShell 2.0
ls -rec | ? {$_.mode -match 'd'} | select FullName, #{N='Count';E={(ls $_.FullName | measure).Count}}
In powershell you can to use severals commands, for looking for this commands digit: Get-Alias;
So the cammands the can to use are:
write-host (ls MydirectoryName).Count
or
write-host (dir MydirectoryName).Count
or
write-host (Get-ChildrenItem MydirectoryName).Count
To count the number of a specific filetype in a folder.
The example is to count mp3 files on F: drive.
( Get-ChildItme F: -Filter *.mp3 - Recurse | measure ).Count
Tested in 6.2.3, but should work >4.

How can I bulk rename files using PowerShell?

I'm trying to recursively rename a bunch of TFS folders using tf rename, PowerShell and a regular expression but I'm having some issues with PowerShell as I haven't spent much time with it. This is what I've put together so far to replace a leading 5 with 2.3.2 but it isn't working:
dir | foreach { tf rename $_ { $_.Name -replace '^5', '2.3.2' } }
Actual result:
Unrecognized command option 'encodedCommand'.
Unrecognized command option 'encodedCommand'.
Unrecognized command option 'encodedCommand'.
Unrecognized command option 'encodedCommand'.
...etc.
Update:
I got a little closer by doing the following instead:
dir | foreach { $newname = $_.Name -replace "^5", "2.3.2"; tf rename $_ $newname }
My next goal is to make this recurse subdirectories but this seems a bit more challenging (changing it to dir -recurse makes it quit after the parent folders for some reason).
I would first filter by 5* so you only process names that start with 5. Also, in this case since tf.exe isn't a PowerShell cmdlet, you don't want to use a scriptblock to determine a new name. Just use a grouping expression like so:
dir -filter 5* | foreach { tf rename $_ ($_.Name -replace '^5', '2.3.2')}
BTW, when you are trying to debug parameter passing to a native EXE like this it is immensely helpful to use the echoargs.exe utilty from the PowerShell Community Extensions. This is what it told me about your original approach:
6# dir -filter 5* | foreach { echoargs rename $_ { $_.Name -replace '^5', '2.3.2' } }
Arg 0 is <rename>
Arg 1 is <5foo.txt>
Arg 2 is <-encodedCommand>
Arg 3 is <IAAkAF8ALgBOAGEAbQBlACAALQByAGUAcABsAGEAYwBlACAAJwBeADUAJwAsACAAJwAyAC4AMwAuADIAJwAgAA==>
Arg 4 is <-inputFormat>
Arg 5 is <xml>
Arg 6 is <-outputFormat>
Arg 7 is <text>
Notes:
TFS has native cmdlets -- no need for tf.exe in most cases.
The time complexity of workspace operations depends on the number of pending renames already in the workspace. In TFS 2005/2008 it's significantly worse than linear. Bottom line, you should really consider batching up renames into multiple checkins if you have a large # of items, otherwise every single "tf rename" (or New-TfsPendingChange -Rename if using the cmdlets) will start taking minutes.
Try this:
dir . | foreach { $newname = $_.Name -replace "^5", "2.3.2"; tf rename $_ $newname }
Running the above commands requires the tf.exe to have been aliased as 'tf'.. or it did on my machine at least.
Run this command:
Set-Alias tf "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\tf.exe"
Update the path to tf.exe as appropriate
Also consider adding the line to your profile for future use
notepad $PROFILE

Resources