Foreach loop in Powershell on the console - powershell

Very simple question here, I want to see how can we process a bunch of commands using foreach on the command line (not through a PS1 script).
For instance, I display the directory listing on the console, now I want to execute 2 commands per object.
Get-ChildItem | ForEach-Object { Write-Host $_ $_}
This is ok, it shows the filename twice, but lets say I wanted to run 2 Write-Host commands for the same object, would that be possible on the console?
PS: I'm trying to achieve writing an output to 2 files using the out-file cmdlet, so I can read something and have 2 separate out-file calls per object
Thanks

you can script in the console windows just as you would in a powershell file. Use the "`" (backtick) key to separate lines. e.g.:
PS > Write-Host `
>>> hello, world!
So you could do
PS > Get-ChildItem | ForEach-Object { `
>>> Write-Host $_ `
>>> doFoo() `
>>> doBar() `
>>> ` }

Basically you want to execute 2 commands in ForEach-Object statement, right?
Just use ; to separate commands in this way ForEach-Object { command1; command2 }
In your code it should be something like this
Get-ChildItem | ForEach-Object { Write-Host $_; Write-Host $_ }

Related

Consolidate 2 Powershell scripts into one

I have two Powershell scripts running at once on my PC doing a similar job. Each one looks for a sting pattern in a log file and then writes a text string to a text file. This updates dynamically so, each time the string changes in the log file, it's updated in the output text file. All works great.
I would like to consolidate both scripts into one Powershell script to make things more efficient. Hope this makes sense. Below is an example of one of the Powershell scripts. Both scripts are the same except the $output file name is different and the $patterns are different. I’d like each string to go the a csv file.
$log = "LogFile.txt"
$output = "ExampleOutput.txt"
$patterns = #("GroupEvents: Linedup for group (.*), subgroup",
"Workout(.*)",
"GroupEvents: Started in group (.*), subgroup",
"Run Device Selected: (.*)")
function ProcessLog {
[CmdletBinding()] Param([Parameter(ValueFromPipeline)]$item)
Write-Output "Running with $item"
foreach($pattern in $patterns)
{
$save = (Select-String -InputObject $item -Pattern $pattern | % {
$_.Matches.Groups[1].Value })
if(-not [string]::IsNullOrWhitespace($save))
{
$save.Trim('(').Trim(')') | Set-Content $output
return
}
}
}
Get-Content -Tail 0 -Wait -Encoding "UTF8" $log | % { $_ | ProcessLog }
I realise this may not be as simple as it sounds and any other ideas are welcome. Perhaps each individual string could be written to a different tab in a comma separated csv

Combine outputs in Powershell

I currently have this script that checks the registry and if the key exists then it will output a value to the console.
How can I modify this script so that it saves each output to a variable and then that variable will be exported to a text/csv file?
if ((Get-ItemPropertyValue -Path "HKLM:\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_HTTP_USERNAME_PASSWORD_DISABLE" -Name HelpPane.exe) -eq '1')
{
Write-Output 'Yes'
}
else
{
Write-Output 'No'
}
if ((Get-ItemPropertyValue -Path "HKLM:\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_DISABLE_SQM_UPLOAD_FOR_APP" -Name iexplore.exe) -eq '1')
{
Write-Output 'Yes'
}
else
{
Write-Output 'No'
}
if ($Host.Name -eq "ConsoleHost")
{
Write-Host "Press any key to continue..."
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
Use Tee-Object for this, which moves data through the pipeline as well as saves it to a file:
$content | Tee-Object -FilePath C:\some\path\on\disk.txt
This will take your variable $content, pipe it to Tee-Object which writes the output to a file, then takes the same output and pushes it through the pipeline. You should see that $content is also written to the output stream in this case but you could also pass it to another cmdlet in the pipeline if you choose to do so.
You have options.
3 ways to store and display PowerShell Variable simultaneously
https://ridicurious.com/2017/06/30/3-ways-to-store-display-results-infrom-a-powershell-variable-at-the-same-time
# Using -OutVariable parameter
Get-Process a* -OutVariable process
# PowerShell Variable squeezing
($process = Get-Process a*)
# Using Tee-Object Cmdlet
Tee-Object Cmdlet T’s results to o/p stream and Variable $process at the same time
Point of note:
Avoid using Write-Host/echo, unless you are using screen text coloring. There is little reason to use it as output to the screen is the PowerShell default.
Also, if you are planning to use data down the line/ pipe, etc, then Write-Host empties the buffer and the data is gone. Well depending on what version of PowerShell you are using.
Resources:
From the creator of Powershell.
Write-Host Considered Harmful
http://www.jsnover.com/blog/2013/12/07/write-host-considered-harmful
... Jeffrey Snover changes his stance on this as of May 2016.
With PowerShell v5 Write-Host no longer "kills puppies". data is
captured into info stream ...
https://twitter.com/jsnover/status/727902887183966208
https://learn.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Utility/Write-Information?view=powershell-5.1
Your code without the Write-Host thing.
if ((Get-ItemPropertyValue -Path 'HKLM:\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_HTTP_USERNAME_PASSWORD_DISABLE' -Name HelpPane.exe) -eq '1')
{'Yes'}
else {'No'}
if ((Get-ItemPropertyValue -Path 'HKLM:\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_DISABLE_SQM_UPLOAD_FOR_APP' -Name iexplore.exe) -eq '1')
{'Yes'}
else { 'No'}
if ($Host.Name -eq "ConsoleHost")
{
'Press any key to continue...'
$Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyUp') > $null
}
Lastly, be cognizant about quoting. Single quotes for simple strings, and double quotes for variable expansion or other specific string handling.
As defined in the help files and other resources:
about_Quoting_Rules - PowerShell | Microsoft Docs
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules
A Story of PowerShell Quoting Rules
https://trevorsullivan.net/2016/07/20/powershell-quoting
Windows PowerShell Quotes
https://www.computerperformance.co.uk/powershell/quotes

How can I write a script that will read other scripts and record their processes?

I have a folder of scripts that are being used for my company, and I need to know what each script does. I am trying to write a script in power shell that will record what each script does into a csv file.
I am a beginner in Powershell and am still learning so I apologize if I am being unclear.
I know that each of these scripts basic function is to map drives to a users computer, but there are too many to go through manually, any advice would be appreciated!
EDIT: Most of them are bat with a couple of vbs too. I want to record what drives are being mapped.
EDIT 2: I have now written my own script that looks like this :
Set-location z:\
get-Childitem "z:\Test"|
Foreach-object{
$filename = $_.Fullname
Get-content $filename|
foreach-object {
if ($_ -match "echo off") {
Write-output "$($filename): $_" | select-object $_ $filename
| export-csv "test.csv" -notypeinformation
}
}
}
I am having trouble exporting the data into a csv file as the error "A positional parameter cannot be found that accepts argument 'z:\Test\Test1.bat'"
The easiest way will be string parsing: look for the commands that map the drives. That's net use for bat files, or MapNetworkDrive in VBS. So look for those lines.
This will look through all the files in a folder and output the filename and the content of the line wherever it finds those lines:
Get-ChildItem "C:\Scripts" |
Foreach-Object {
$filename = $_.FullName
Get-Content $filename |
Foreach-Object {
if ($_ -match "net use" -or $_ -match "MapNetworkDrive") {
Write-Output "$($filename): $_"
}
}
}
That will not likely be exactly what you need, but it should get you started.

ps1 not giving result in dos shell

My script....
function DirSize {
BEGIN {}
PROCESS
{
$timer = Get-Date -UFormat "%Y%m%d%H%M%S"
$bFile="C:\00mpd\DR\mrv_incr_"+$timer+".txt"
$data = Get-Content "C:\00mpd\DR\vaults.txt"
write-host $data.count total lines read from file
foreach ($line in $data)
{
write-host $line
$a=0
foreach ($file in (Get-ChildItem $line -recurse -Force| where-object {!($_.psiscontainer)} | where { $_.LastWriteTime -ge ((get-date).AddHours(-1))} ))
{
$a+=$file.length/1GB
}
Write-Output "$line :: $a GB"
"$line :: $a GB"| Out-File $bFile -append
}
}
END {}
} # end function Get-DirSize
vaults.txt contents name of the various folders....
d:\Milind_laptop_backup\DDrive\Milind_pst\
d:\Milind_laptop_backup\DDrive\Office\
d:\Milind_laptop_backup\DDrive\personal\
d:\Milind_laptop_backup\DDrive\ToMilind\
Above ps1 give proper result when running in powershell window. But when I'm running same in dos shell, it's not giving any result.
Powershell commands
PS C:\> . "c:\00mpd\dr\dirsize.ps1"
PS C:\> DirSize
But when I tried in Dos shell, no result is coming out.
C:\00mpd\DR>powershell -File "c:\00mpd\dr\DirSize.ps1"
You are running the script as function
This is OK when you are executing it from Inside PowerShell, The Function DirSize Load it and DirSize to Execute it. but when run it from .ps1 file it just loads the function but not executing it.
To Solve this:
Method 1:
Remove the function DirSize { and the last line } # end function Get-DirSize
Method 2:
Dot Source the .ps1 as a command to load the function then run it after
powershell -Command ". c:\00mpd\dr\DirSize.ps1; DirSize"
As Mathias R. Jessen Suggested
As others have said if you remove
function DirSize{ }
and dot source your script, it will solve one issue.
I would personally then change Write-Output to Write-Host. It should give the result that you are looking for.

How can I pipe from Get-Content -Wait?

I would like to pipe the output of Get-Content $file -Wait to a custom PowerShell script. The script looks like this.
$lines = ($input | Out-String) -replace "`r", "" -split "`n"
foreach ($line in $lines) {
#Process $line
Write-Host $line
}
Basically the idea is to take the input, format it nicely and then process the output before it gets printed to the console.
The problem is nothing is getting sent to my script when I call it like cat $file -Wait | MyScript. If I do cat $file -Wait or cat $file | MyScript, everything works as expected. But combining the pipe and the wait parameter doesn't work.
Is there some syntax I need to use to allow processing the -Wait parameter? I tried using Out-String -Stream, but that doesn't work either.
The problem is with $input.
If you do this :
Get-Content $file -Wait | Get-Member -InputObject $input
Or
Get-Content $file -Wait | Get-Member -InputObject $_
You will get :
Get-Member : 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.
If Get-Member is unable to read the object going through the pipeline, you know that something is very wrong with the object (or the pipelining).
Let's try piping $input to Out-String, like you are doing in your script :
Get-Content $file -Wait | Out-String $input
You will get :
Out-String : A positional parameter cannot be found that accepts argument 'System.Collections.ArrayList+ArrayListEnumeratorSimple'.
At line:1 char:52
+ get-content '.\netstat anob.txt' -wait | Out-String <<<< $input
+ CategoryInfo : InvalidArgument: (:) [Out-String], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.OutStringCommand
So, indeed, "Get-Content" -Wait gives you a weird kind of object : a System.Collections.ArrayList+ArrayListEnumeratorSimple .
It looks like it's the result of the GetEnumerator() method from a System.Collections.ArrayList object, or something like that.
Given the fact that Get-Member or even "Get-Member -Force" is unable to read this kind of "Object", from Powershell's point of view, it's not a object.
The workaround would be to drop the -Wait parameter from Get-Content and find another way of achieving what you want, possibly by running Get-Content and then, running "Get-Content -Tail 1" several times in a loop.
This is possible if your script accepts pipeline input. You can see it as you have mentioned when you pipe to other cmdlets like Select-String. For example defining script.ps1 as:
process { Write-Host "line: $input" }
Then running
1..200 | foreach { add-content -Path test.txt -Value "$_"; start-sleep 1 }
in one PowerShell session and
gc test.txt -wait | .\script.ps1
in another, you can see that each line is piped to the script.
I don't see any way to do what you are asking. -Wait initiates a loop that never ends, the only way to stop is to manually kill it. Since it will always be stuck inside the loop anything you try to do after initiating the loop is never going to process.
The problem is in this line:
Write-Host $line
You should use Write-Output instead. Write-Output sends objects to pipeline, Write-Host directly to host (console).