Out-File inside a foreach statement - powershell

im trying to run this script, it works but every time that it changes from item on the list it erases the file and starts over.
what i need this to do is to get from the list of items all the users and put them all in a single text file.
$sitios = Get-Content -Path C:\sitios.txt
Connect-SPOService -Url https://aaa.sharepoint.com/ -Credential Admin#aaa.onmicrosoft.com
foreach ($sitio in $sitios){
$sitio
Get-SPOUser -Site $sitio -Limit ALL | Out-File -FilePath C:\usuarios.txt
}
any help is appreciated

You could use the -Append switch of Out-File but all you should have to do is move it outside the loop.
(foreach ($sitio in $sitios){
Write-Host $sitio
Get-SPOUser -Site $sitio -Limit ALL
}) | Out-File -FilePath C:\usuarios.txt
That way all output will be sent to Out-File. I added Write-Host $sitio so that was not going to be in the file. You could also use Set-Content which is considered the preferential choice over Out-File
The brackets are needed around the loop so that we can use the pipe output. That foreach construct cannot have data directly piped from it. An answer here covers the reason.
That all being said you could then do something like this
$sitios | ForEach-Object{
Write-Host $_
Get-SPOUser -Site $_ -Limit ALL
} | Set-Content C:\usuarios.txt
This output should be a complex object that might not have a proper tostring equivalent. You can still get all the data with something like this instead.
$sitios | ForEach-Object{
Write-Host $_
Get-SPOUser -Site $_ -Limit ALL
} | Export-CSV C:\usuarios.txt -NoTypeInformation

You can use the Add-Content Cmdlet:
https://technet.microsoft.com/en-us/library/ee156791.aspx
Instead of Out-File use Add-Content
as in:
[...] | Add-Content C:\usuarios.txt

Related

Powershell Out-File output all on the same line

I have a powershell script that populates a variable, $Users, from the contents of a text file using the Get-Content cmdlet. I then want to append this information to the end of a different text file using Out-File. However, currently the output is appended all in a row. What I need is for each string to be on it's own line.
I have tried piping the variable into the Write-Output cmdlet and it displays correctly on the screen, but when I redirect it from Write-Output back to the Out-File cmdlet it appends the information all in a row again.
$Users = Get-Content "C:\Users\XXXX\Desktop\Password Reset\Users5.txt"<br>
Out-File -InputObject $Users -FilePath "C:\Users\XXXX\Desktop\Password Reset\RefUsers.txt"
If it was me I would use Add-Content for this with a pipe.
$Users = Get-Content -Path "C:\Users\XXXX\Desktop\Password Reset\Users5.txt"
$Users | Add-Content -Path "C:\Users\XXXX\Desktop\Password Reset\RefUsers.txt"
Pay attention to encoding. Add-Content uses ascii by default I believe. Also if you are not doing anything with the data you can skip the variable all together.
GC "C:\Users\XXXX\Desktop\Password Reset\Users5.txt" |
AC "C:\Users\XXXX\Desktop\Password Reset\RefUsers.txt"
Gc being an alias for Get-Content and Ac for Add-Content
Here is how my final script turned out:
$Users = Get-Content "C:\Users\XXXX\Desktop\Password Reset\Users5.txt"
Foreach($U in $Users){
Add-Content "C:\Users\XXXX\Desktop\Password Reset\RefUsers1.txt" "`n$U"
}
Thanks Matt!

Piping to More Than One Location - PowerScript

I want to do something like this-
"Error array cleared." | Out-File $ErrorLog $InfoLog -Append
However it's not working. Is this possible without writing another line to output it to the other file?
You can also use Tee-Object to accomplish the same thing. Look at example 3 on that page. Here is a quick sample that grabs the contents of the current directory and saves it to two files.
Get-ChildItem | Tee-Object -FilePath teetest.txt | Out-File teetest2.txt
One way is with a short function like this:
function Out-FileMulti {
param(
[String[]] $filePath
)
process {
$text = $_
$filePath | foreach-object {
$text | out-file $_ -append
}
}
}
Example:
"Out-FileMultiTest" | Out-FileMulti "test1.log","test2.log"
(Writes the string "Out-FileMultiTest" to both test1.log and test2.log)
Found this snippet of code which does what I need-
"Test" | %{write-host $; out-file -filepath $ErrorLog -inputobject $ -append}

The following is not increment the file count correctly Any Clue as to why?

This powershell code searches the directory and outputs a list of all the files and how old they are to a log file that is parsed buy a different script. all that is working correctly but i also need to keep track of the number of files it found for that dir and the number of files found globally. Thats what the two foreach-Object statements do. but they are staying at 0.
gci -filter *.avi | Select-Object Name, #{Name="Age"; Expression= { (((Get-Date) - $_.CreationTime).Days) }} | Where {$_.Age -ge $daysToKeep} | Out-File -filepath $logFile -append | Foreach-Object {$fileCountCam1++} | Foreach-Object {$fileCount++}
mjolinor's solution is valid, but there's another way (if you can use v3). You can use Tee-Object to write to the file without a loop.
You can also combine your two variable increments into the same script block in the final foreach-object which will speed things up significantly.
gci -filter *.avi |
Select-Object Name, #{Name="Age"; Expression= { (((Get-Date) - $_.CreationTime).Days) }} |
Where {$_.Age -ge $daysToKeep} | Tee-Object -filepath $logFile -append |
Foreach-Object {$fileCountCam1++;$fileCount++}
Out-File is a termnating cmdlet (it doesn't ouput the object to the pipeline), so everything after it isn't getting any input from the pipeline.
See if this works better:
gci -filter *.avi |
Select-Object Name, #{Name="Age"; Expression= { (((Get-Date) - $_.CreationTime).Days) }} |
Where {$_.Age -ge $daysToKeep} |
Foreach-Object {
$_ | Out-File -filepath $logFile -append
$fileCountCam1++
$fileCount++
}

Powershell Foreach-Object Foreach loop read string from several files and write to one

I have trouble to use several commands in one Foreach or Foreach-Object loop
My situation is -
I have many text files, about 100.
So they are read Get-ChildItem $FilePath -Include *.txt
Every file's structure is same only key information is different.
Example
User: Somerandomname
Computer: Somerandomcomputer
With -Replace command I remove "User:" and "Computer:" so $User = Somerandomname and $computer = "Somerandomcomputer.
In each circle $user and $Computer with -Append should be written to one file. And then next file should be read.
foreach-object { $file = $_.fullname;
should be used, but I can not figure out the right syntax for it. Could someone help me with it?
Assuming you've defined $FilePath, $user, and/or $computer elsewhere, try something like this.
$files = Get-ChildItem $FilePath\*.txt
foreach ($file in $files)
{
(Get-Content $file) |
Foreach-Object { $content = $_ -replace "User:", "User: $user" ; $content -replace "Computer:", "Computer: $computer" } |
Set-Content $file
}
You can use ; to delimit additional commands in within the Foreach-Object, for example if you wanted to have separate commands for your user and computer name. If you don't enclose the Get-Content cmdlet with parenthesis you will get an error because that process will still have $file open when Set-Content tries to use it.
Also note that with Powershell, strings in double quotes will evaluate variables, so you can put $user in the string to do something like "User: $user" if you so desired.
Try this:
gci $FilePath -Include *.txt | % {
gc $_.FullName | ? { $_ -match '^(?:User|Computer): (.*)' } | % { $matches[1] }
} | Out-File 'C:\path\to\output.txt'
If User and Computer are on separate lines, you need to read the lines two at a time. The ReadCount parameter of Get-Content allows you to do that.
Get-ChildItem $FilePath -Include *.txt `
| Get-Content -ReadCount 2 `
| %{ $user = $_[0] -replace '^User: ', ''; $computer = $_[1] -replace '^Computer: ', ''; "$user $computer" } `
| Out-File outputfile.txt
This makes the assumption that every file contains only lines of the exact form
User: someuser
Computer: somecomputer
User: someotheruser
Computer: someothercomputer
...
If this is not the case, you will need to provide whatever is the exact file format.

How do I add a newline to command output in PowerShell?

I run the following code using PowerShell to get a list of add/remove programs from the registry:
Get-ChildItem -path hklm:\software\microsoft\windows\currentversion\uninstall `
| ForEach-Object -Process { Write-Output $_.GetValue("DisplayName") } `
| Out-File addrem.txt
I want the list to be separated by newlines per each program. I've tried:
Get-ChildItem -path hklm:\software\microsoft\windows\currentversion\uninstall `
| ForEach-Object -Process { Write-Output $_.GetValue("DisplayName") `n } `
| out-file test.txt
Get-ChildItem -path hklm:\software\microsoft\windows\currentversion\uninstall `
| ForEach-Object {$_.GetValue("DisplayName") } `
| Write-Host -Separator `n
Get-ChildItem -path hklm:\software\microsoft\windows\currentversion\uninstall `
| ForEach-Object -Process { $_.GetValue("DisplayName") } `
| foreach($_) { echo $_ `n }
But all result in weird formatting when output to the console, and with three square characters after each line when output to a file. I tried Format-List, Format-Table, and Format-Wide with no luck. Originally, I thought something like this would work:
Get-ChildItem -path hklm:\software\microsoft\windows\currentversion\uninstall `
| ForEach-Object -Process { "$_.GetValue("DisplayName") `n" }
But that just gave me an error.
Or, just set the output field separator (OFS) to double newlines, and then make sure you get a string when you send it to file:
$OFS = "`r`n`r`n"
"$( gci -path hklm:\software\microsoft\windows\currentversion\uninstall |
ForEach-Object -Process { write-output $_.GetValue('DisplayName') } )" |
out-file addrem.txt
Beware to use the ` and not the '. On my keyboard (US-English Qwerty layout) it's located left of the 1.
(Moved here from the comments - Thanks Koen Zomers)
Give this a try:
PS> $nl = [Environment]::NewLine
PS> gci hklm:\software\microsoft\windows\currentversion\uninstall |
ForEach { $_.GetValue("DisplayName") } | Where {$_} | Sort |
Foreach {"$_$nl"} | Out-File addrem.txt -Enc ascii
It yields the following text in my addrem.txt file:
Adobe AIR
Adobe Flash Player 10 ActiveX
...
Note: on my system, GetValue("DisplayName") returns null for some entries, so I filter those out. BTW, you were close with this:
ForEach-Object -Process { "$_.GetValue("DisplayName") `n" }
Except that within a string, if you need to access a property of a variable, that is, "evaluate an expression", then you need to use subexpression syntax like so:
Foreach-Object -Process { "$($_.GetValue('DisplayName'))`r`n" }
Essentially within a double quoted string PowerShell will expand variables like $_, but it won't evaluate expressions unless you put the expression within a subexpression using this syntax:
$(`<Multiple statements can go in here`>).
I think you had the correct idea with your last example. You only got an error because you were trying to put quotes inside an already quoted string. This will fix it:
gci -path hklm:\software\microsoft\windows\currentversion\uninstall | ForEach-Object -Process { write-output ($_.GetValue("DisplayName") + "`n") }
Edit: Keith's $() operator actually creates a better syntax (I always forget about this one). You can also escape quotes inside quotes as so:
gci -path hklm:\software\microsoft\windows\currentversion\uninstall | ForEach-Object -Process { write-output "$($_.GetValue(`"DisplayName`"))`n" }
Ultimately, what you're trying to do with the EXTRA blank lines between each one is a little confusing :)
I think what you really want to do is use Get-ItemProperty. You'll get errors when values are missing, but you can suppress them with -ErrorAction 0 or just leave them as reminders. Because the Registry provider returns extra properties, you'll want to stick in a Select-Object that uses the same properties as the Get-Properties.
Then if you want each property on a line with a blank line between, use Format-List (otherwise, use Format-Table to get one per line).
gci -path hklm:\software\microsoft\windows\currentversion\uninstall |
gp -Name DisplayName, InstallDate |
select DisplayName, InstallDate |
fl | out-file addrem.txt
The option that I tend to use, mostly because it's simple and I don't have to think, is using Write-Output as below. Write-Output will put an EOL marker in the string for you and you can simply output the finished string.
Write-Output $stringThatNeedsEOLMarker | Out-File -FilePath PathToFile -Append
Alternatively, you could also just build the entire string using Write-Output and then push the finished string into Out-File.