Inline command execution in PowerShell ForEach Loop - powershell

I am familiar with how BASH syntax handles this request but I am unable to find a way to do this in PowerShell.
BASH Example:
for x in `ls .`; do something with ${x}; done
What I am trying to perform the same thing with PowerShell (which has the incorrect syntax for demonstration purposes)...
ForEach ($group in `$wsus.GetComputerTargetGroups()`)
...I obviously get a syntax error.
BASH to PowerShell translation is the ask here. :D

For the example you have, it would just work (without the backticks):
ForEach ($group in $wsus.GetComputerTargetGroups()) {
# do stuff
}
If it's a command, you can wrap it in a subexpression:
foreach ($process in $(Get-Process)) {
# do stuff
}
You might also look at the ForEach-Object cmdlet which can be more idiomatic in PowerShell's pipelines:
Get-Process | ForEach-Object { Write-Verbose "This process is $_" -Verbose }

This might help you get going...
$things = Get-ChildItem C:\Windows\
foreach ($thing in $things) {
# Do a thing with each one in turn
Write-Host $thing.Name -ForegroundColor Magenta
}

Related

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.

Please explain, function to add string to first line of a file

Would you like to explain what is happing in the PowerShell code at the bottom of this post?
I got my first, lets say "hello world" in PowerShell, and it needed these lines of code. It works like a charm, but I am not sure what it does, exactly.
The questions starts at
$( ,$_; Get-Content $Path -ea SilentlyContinue) | Out-File $Path
So this is what I understand so far.
We create a function called Insert-Content. With the params (input that will be interpeted as a string and will be added to $path).
function Insert-Content {
param ( [String]$Path )
This is what the function does/processes:
process {
$( ,$_;
I am not sure what this does, but I guess it gets "the input" (the "Hello World" before the | in "Hello World" | Insert-Content test.txt).
And then we got -ea SilentylyContinue, but what does it do?
process {
$( ,$_; Get-Content $Path -ea SilentlyContinue) | Out-File $Path
It would be greatly appreciated if you could explain these two parts
$( ,$_;
-ea SilentylyContinue
Code needed/used: Add a string to the first line of a doc.
function Insert-Content {
param ( [String]$Path )
process {
$( ,$_;Get-Content $Path -ea SilentlyContinue) | Out-File $Path
}
}
"Hello World" | Insert-Content test.txt
process {...} is used for applying the code inside the scriptblock (the {...}) to each parameter argument that the function reads from a pipeline.
$_ is an automatic variable containing the current object. The comma operator , preceding the $_ converts the string value to a string array with a single element. It's not required, though. The code would work just as well with just $_ instead of ,$_.
Get-Content $Path reads the content of the file $Path and echoes it as to the success output stream as an array of strings (each line as a separate string).
The ; separates the two statements ,$_ and Get-Content $Path from each other.
| Out-File $Path writes the output back to the file $Path.
The subexpression operator $() is required to decouple reading the file from writing to it. You can't write to a file when a process is already reading from it, so the subexpression ensures that reading is completed before writing starts.
Basically this whole construct
$( ,$_;Get-Content $Path -ea SilentlyContinue) | Out-File $Path
echoes the input from the pipeline (i.e. the "Hello World") followed by the current content of the file $Path (effectively prepending the input string to the file content) and writes everything back to the file.
The -ea SilentlyContinue (or -ErrorAction SilentlyContinue) suppresses the error that would be thrown when $Path doesn't already exist.
The relevant section of code must be handled as a whole:
$( ,$_;Get-Content $Path -ea SilentlyContinue) | Out-File $Path
First, as others have said, -ea is the shortened version of -ErrorAction. -ErrorAction SilentlyContinue tells the cmdlet "Suppress any error messages and continue executing." See Get-Help about_Common_Parameters -ShowWindow.
Next, the $() is the sub-expression operator. It means "Evaluate what is between the parentheses as its own command and return the result(s)." See Get-Help about_Operators -ShowWindow.
This subexpression here is:
,$_;Get-Content $Path -ea SilentlyContinue
It contains two statements: ,$_ and Get-Content $Path -ea SilentlyContinue. The semicolon is just the end of statement identifier to separate the two.
,$_; is two kind of complex parts.
$_ is the special pipeline variable. It always contains whatever object is in the current pipeline. See Get-Help about_Automatic_Variables -ShowWindow for more about $_ (it's mostly used with ForEach-Object and Where-Object cmdlets, so check those out, too), and Get-Help about_pipelines -ShowWindow for more help with pipelines.
The comma here is the comma operator (see Get-Help about_Operators -ShowWindow again). It creates an array from the objects on either side. For example, 1,2,3 creates an array with three elements 1, 2, and 3. If you want a two item array, you can say 1,2.
What if you want a one item array? Well, you can't say 1, because Powershell will think you forgot something. Instead, you can say ,1.
You can test it with the -is operator:
PS C:\> 1,2,3 -is [Array]
True
PS C:\> 1 -is [Array]
False
PS C:\> ,1 -is [Array]
True
Why might you want to create a one item array? Well, if later on your code is assuming the item is an array, it can be useful. In early editions of Powershell, properties like .Count would be missing for single items.
For completeness, yes, I believe you could write:
$( #($_);Get-Content $Path -ea SilentlyContinue)
And I think you could rewrite this function:
function Insert-Content {
param ( [String]$Path )
process {
#Read from pipeline
$strings = #($_);
#Add content of specified file to the same array
$strings += Get-Content $Path -ea SilentlyContinue;
#Write the whole array to the file specified at $Path
$strings | Out-File $Path;
}
}
So this adds content from the pipeline to the start of a file specified by -Path.
It's also somewhat poor practice not to create a parameter for the pipeline object itself and define it. See... well, see all the topics under Get-Help "about_Functions*", but mostly the Advanced ones. This is an advanced topic.

Local Groups and Members

I have a requirement to report the local groups and members from a specific list of servers. I have the following script that I have pieced together from other scripts. When run the script it writes the name of the server it is querying and the server's local group names and the members of those groups. I would like to output the text to a file, but where ever I add the | Out-File command I get an error "An empty pipe element is not allowed". My secondary concern with this script is, will the method I've chosen the report the server being queried work when outputting to a file. Will you please help correct this newbies script errors please?
$server=Get-Content "C:\Powershell\Local Groups\Test.txt"
Foreach ($server in $server)
{
$computer = [ADSI]"WinNT://$server,computer"
"
"
write-host "==========================="
write-host "Server: $server"
write-host "==========================="
"
"
$computer.psbase.children | where { $_.psbase.schemaClassName -eq 'group' } | foreach {
write-host $_.name
write-host "------"
$group =[ADSI]$_.psbase.Path
$group.psbase.Invoke("Members") | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
write-host **
write-host
}
}
Thanks,
Kevin
You say that you are using Out-File and getting that error. You don't show_where_ in your code that is being called from.
Given the code you have my best guess is that you were trying something like this
Foreach ($server in $server){
# All the code in this block
} | Out-File c:\pathto.txt
I wish I had a technical reference for this interpretation but alas I have not found one (Think it has to do with older PowerShell versions). In my experience there is not standard output passed from that construct. As an aside ($server in $server) is misleading even if it works. Might I suggest this small change an let me know if that works.
$servers=Get-Content "C:\Powershell\Local Groups\Test.txt"
$servers | ForEach-Object{
$server = $_
# Rest of code inside block stays the same
} | Out-File c:\pathto.txt
If that is not your speed then I would also consider building an empty array outside the block and populate is for each loop pass.
# Declare empty array to hold results
$results = #()
Foreach ($server in $server){
# Code before this line
$results += $group.psbase.Invoke("Members") | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
# Code after this line
}
$results | Set-Content c:\pathto.txt
Worthy Note
You are mixing Console output with standard output. depending on what you want to do with the script you will not get the same output you expect. If you want the lines like write-host "Server: $server" to be in the output file then you need to use Write-Output

Foreach loop in Powershell on the console

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 $_ }