Powershell replacing shortcut target - powershell

can someone please check this script and point me to the right direction. I'm not getting any errors though but script is not working as expected. I'm trying to achieve a shortcut target changed based on logged user.My powershell skills are basic and i'm sure must be missing some logic here:-
#$shell = new-object -com wscript.shell
$loguser="username"
$link ="test1.lnk"
$oldtarget=$link.tragetpath
$oldpath="c:\notepad.exe"
Get-ChildItem -Filter $link -Recurse
if ($oldtarget -eq $oldpath)
{
$csvfile=Import-csv "c:\test.csv"
$newtarget=$row.newpath
$user=$row.user
(get-Content $csvfile) | foreach-object {$_.$user -match $loguser} | -replace $oldtarget $newtarget
}
$link.SaveInfo

A way better way to modify shortcuts is to get rid of the VBScript shell and use P/Invoke. Take a look at sample code in Poshcode. It contains some 1200 lines of code, so I won't copy it here.

Related

Powershell - clarification about foreach

I am learning powershell and I need someone to give me an initial push to get me through the learning curve. I am familiar with programming and dos but not powershell.
What I would like to do is listing all files from my designated directory and pushing the filenames into an array. I am not very familiar with the syntax and when I tried to run my test I was asked about entering parameters.
Could someone please enlighten me and show me the correct way to get what I want?
This is what powershell asked me:
PS D:\ABC> Test.ps1
cmdlet ForEach-Object at command pipeline position 2
Supply values for the following parameters:
Process[0]:
This is my test:
[string]$filePath = "D:\ABC\*.*";
Get-ChildItem $filePath | foreach
{
$myFileList = $_.BaseName;
write-host $_.BaseName
}
Why was ps asking about Process[0]?
I would want to ps to list all the files from the directory and pipe the results to foreach where I put each file into $myFileList array and print out the filename as well.
Don't confuse foreach (the statement) with ForEach-Object (the cmdlet). Microsoft does a terrible job with this because there is an alias of foreach that points to ForEach-Object, so when you use foreach you have to know which version you're using based on how you're using it. Their documentation makes this worse by further conflating the two.
The one you're trying to use in your code is ForEach-Object, so you should use the full name of it to differentiate it. From there, the issue is that the { block starts on the next line.
{} is used in PowerShell for blocks of code related to statements (like while loops) but is also used to denote a [ScriptBlock] object.
When you use ForEach-Object it's expecting a scriptblock, which can be taken positionally, but it must be on the same line.
Conversely, since foreach is a statement, it can use its {} on the next line.
Your code with ForEach-Object:
Get-ChildItem $filePath | ForEach-Object {
$myFileList = $_.BaseName;
write-host $_.BaseName
}
Your code with foreach:
$files = Get-ChildItem $filePath
foreach ($file in $Files)
{
$myFileList = $file.BaseName;
write-host $file.BaseName
}

Powershell - net use doesn't work with variables

I'm trying to get a script running that maps network drives at login. Generally, I use get-content with | convertfrom-stringdata to put my parameters (drive letter and path) in a hash.
My problem is the following:
net use /persistent:no $driveletter $networkpath
results with an error. When I replace $networkpath with the actual path (\\server\share\folder), it works.
Does anyone know what to do there? Help is greatly appreciated.
If any information is missing, I'll add it as soon as I can!
Greetings,
Blaargh
EDIT: more code for better understanding of problem
$hash = get-content C:\temp\file.txt | convertfrom-stringdata
foreach ($keys in $hash.keys) {
$hashtwo = $hash.$keys -split ("=")
net use /persistent:no $hashtwo[1] $hashtwo[0]
}
My textfile looks like this:
key = \\\\server\\share\\folder =G:
PetSerAl found the solution:
#blaargh Add Write-Host "'$($hashtwo[1])' '$($hashtwo[0])'" to be sure, that variables does not have extra space somewhere.

get report of all open explorer windows

I want to get a report of all open explorer windows titles and current paths. The current paths part of this is problem is answered here with C#, but I want this for powershell and am not sure how to adapt it.
I am not sure how to bring out the window titles part of it.
Could someone please assist.
Sounds to me like you're looking for something like this:
$app = New-Object -COM 'Shell.Application'
$app.Windows() | Select-Object LocationURL
AFAICS the window objects don't have a title property, but you can get that information from Get-Process via the window handle ID:
function Get-WindowTitle($handle) {
Get-Process |
Where-Object { $_.MainWindowHandle -eq $handle } |
Select-Object -Expand MainWindowTitle
}
$app = New-Object -COM 'Shell.Application'
$app.Windows() |
Select-Object LocationURL, #{n='Title';e={Get-WindowTitle $_.HWND}}
Ansgar Wiechers' answer is helpful, but the title of File Explorer windows doesn't necessarily contain the full path of the location (folder) being displayed.
Somewhat obscurely, it is the .Document.Folder.Self.Path property of the window objects returned by the .Windows() method of the Shell.Application COM object that contains the full, local or UNC path.
Therefore, the following lists the full paths of all open Explorer windows:
(New-Object -ComObject 'Shell.Application').Windows() | ForEach-Object {
$_.Document.Folder.Self.Path
}
Note: Special locations such as File Explorer's "Quick access" are represented by ::-prefixed GUIDs; e.g., ::{679F85CB-0220-4080-B29B-5540CC05AAB6}

Use of property names in PowerShell pipeline $_ variable

As a C# developer, I'm still learning the basics of PowerShell and often getting confused.
Why does the $_. give me the intellisense list of vaild property names in the first example, but not the second?
Get-Service | Where {$_.name -Match "host" }
Get-Service | Write-Host $_.name
What is the basic difference in these two examples?
When I run the second one, it gives this error on each iteration of Get-Service:
Write-Host : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters
that take pipeline input.
At line:3 char:15
+ Get-Service | Write-Host $_.name
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (wuauserv:PSObject) [Write-Host], ParameterBindingException
+ FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.WriteHostCommand
My coworker and I were first using a foreach loop to iterate a Get-commandlet, and we were stuck getting the property names to appear. We tried to simplify till we go to the core basics above.
Just figured out sometimes it's a typo in the commandlet, below first one fails because the commandlet should be Get-Service instead of Get-Services.
foreach ($item in Get-Services)
{
Write-Host $item.xxx #why no intellisense here?
}
foreach ($item in Get-Service)
{
Write-Host $item.Name
}
First part: you can't use $_ like that, it represents current pipeline object only within script blocks. These script blocks are usually used with any *-Object cmdlet (but there are other use cases too). Not all parameters/ cmdlet support it. Write-Host is one of those that don't.
Second: It looks like you are using own function (GetServices). PowerShell v3 intellisense is depending on command metadata (OutputType). If any cmdlet/ function produces object but is silent about OutputType, intellisense won't work. It's pretty simple to get it, and you can lie and still get proper intellisense for any existing type:
function Get-ServiceEx {
[OutputType('System.ServiceProcess.ServiceController')]
param ()
'This is not really a service!'
}
(Get-ServiceEx).<list of properties of ServiceController object>
You can read more about it on my blog.
Intellisense will work if you put $_ inside a scriptblock.
The following will trigger intellisense:
Get-Service | Foreach-Object {$_.Name} # Intellisense works
Get-Service | Where-Object {$_.Name} # Intellisense works
Get-Service | Write-Host {$_.Name} # Intellisense works
Note that your command need not be a valid command: the third example will not work but intellisense will display auto-complete for $_ anyway because it is inside a scriptblock.
I suspect it is because $_ is only usable inside a scriptblock (e.g. switch, %, ?) but I have no hard-evidence for it.
$_ is the current object in the pipeline. It's the same as $item in a foreach ($item in $array).
Get-Service | Where {$_.name -Match "host" }
Get-Service | Write-Host $_.name
The difference between these two lines is a fundamental part of the PowerShell design. Pipelines are supposed to be easy. You should be able to ex. search for and delete files as simply as:
Get-ChildItem *.txt | Remove-Item
This is clean, simple, and everyone can use it. The cmdlets are built to identify the type of the incoming object, adapt to support the specific type, and process the object.
However, sometimes you need more advanced object manipulation in the pipeline, and that's where Where-Object and Foreach-Object comes in. Both cmdlets lets you write your own processing logic without having to create an function or cmdlet first. To be able to access the object in your code(processing logic), you need an identifier for it, and that is $_. $_ is also supported in some other special cmdlets, but it's not used in most cmdlets(including Write-Host).
Also, Intellisense in foreach ($item in Get-Service) works. You had a typo.

Replace text between quotes in XML

I'm trying to find a way to change unknown text (could be anything) in an XML file for a printer migration.
The text that is in question is
PrintProcessor="hpcpp111"
The Print Processor section could contain anything as this varies depending on printer model, type and driver. I would prefer to use powershell if possible as I am trying to understand scripting and how it all works, but I find it a little confusing. I may need to manually edit thousands of these files as we are migrating 5,500 printers.
I have found some code that gets it close for example
function Reset-InfoPathTemplateLink {
Param(
[string]$FilePath,
[string]$FileExtension,
[string]$OldPath,
[string]$NewPath
)
$files = Get-ChildItem $FilePath -Filter $FileExtension
foreach ($file in $files) {
(Get-Content $file.fullname) |
ForEach-Object {$_ -replace $OldPath,$NewPath} |
Set-Content $file.fullname
} #end foreach
} #end function
and if I dot-source it and then run this command
Reset-InfoPathTemplateLink -FilePath "c:\test2" -FileExtension ".xml" -OldPath "PrintProcessor=""" -NewPath "PrintProcessor='"WinPrint"'"
The WinPrint gets added to the beginning of the current print processor, which isnt ideal. If anyone has tips on how to get this to work, or if they can suggest a better method of doing this I would appreciate it
this is how you can use PowerShell to replace a pattern within a text file:
$path="c:\myPath.txt"
(Get-Content $path) -replace '(PrintProcessor=")(.*)(")','$1WinPrint$3' | Set-Content $path