PowerShell Tee-Object without overwriting file - powershell

I'm trying to create an application that puts variables in a file (minedown.conf) using Tee-Object, but every time it goes to add something to the file it overwrites it. I'm using
$account = Read-Host "Enter your Account SID number"
"account = $account" | Tee-Object -FilePath c:\minedown\minedown.conf
$token = Read-Host "Enter your Authority Token"
"token = $token" | Tee-Object -FilePath c:\minedown\minedown.conf
$from = Read-Host "Enter your Twilio number"
"from - $from" | Tee-Object -FilePath c:\minedown\minedown.conf
I'm trying to make each of those a separate line.

As an aside, in PowerShell 3.0, the -Append switch was added to the Tee-Object cmdlet.

Tee-Object is not the CmdLet you are looking for, try Set-content and Add-Content.
$account = Read-Host "Enter your Account SID number"
"account = $account" | Set-content -Path c:\minedown\minedown.conf
$token = Read-Host "Enter your Authority Token"
"token = $token" | Add-Content -Path c:\minedown\minedown.conf
$from = Read-Host "Enter your Twilio number"
"from - $from" | Add-Content -Path c:\minedown\minedown.conf
The purpose of Tee-Object is really to act as a 'T', in a pipe sequence, in order to send data from the input to output and to a file or a variable (in order to debug a pipe sequence for exemple).

As mentioned, Tee-Object (alias tee) is for splitting output into two directions. On Linux (tee) it is useful for going to screen & file. In PowerShell it is more for putting to screen and throwing it back on the pipeline as well as other stuff, but cannot do Append. Not really how you want it.
However, I needed to do the Linux way and have it show on the screen as well as write to a file (in append mode). So I used the below method to write it onto the pipeline first, then put it to the screen (with colors) and put it in a file that is being appended to rather than just overwritten. Maybe it will be useful to someone:
Write-Output "from - $from" | %{write-host $_ -ForegroundColor Blue; out-file -filepath c:\minedown\minedown.conf -inputobject $_ -append}

Related

I need to display my result in three columns using power shell script

I have my current script display results but when it comes up it just puts everything in one long column but they are in order. Any ideas how I can get three columns called "computer name" "Patch#" "Installed?".
Below is my code
$strCategory = "computer"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$ObjSearcher.SearchRoot = $objDomain
$objSearcher.filter = ("(objectCategory=$strCategory)")
$colProplist = "name"
foreach($i in $colProplist){
$objSearcher.PropertiesToLoad.Add($i)
}
#Finds all operating systems and computer names
$colResults = $objSearcher.FindAll()
foreach($objResult in $colResults){
$objComputer = $objResult.Properties;
$names = $objComputer.name
# Define Hotfix to check
$CheckKBS = #("KB2937610", "KB4019264")
# Query the computers for the HotFix
foreach($name in $names){
foreach ($CheckKB in $CheckKBS) {
$HotFixQuery = Get-HotFix -ComputerName $name | Where-Object {$_.HotFixId -eq $CheckKB} | Select-Object -First 1;
if($HotFixQuery -eq $null) {
Out-file C:\Users\xxx\Desktop\12.csv -Append -InputObject "computername: $name"
Out-file C:\Users\xxx\Desktop\12.csv -Append -InputObject "“KB Patch: $CheckKB"
Out-file C:\Users\xxx\Desktop\12.csv -Append -InputObject "Hotfix $CheckKB is not installed on $name"
} else {
Out-file C:\xxx\xxx\xxx\12.csv -Append -InputObject "computername: $name"
Out-file C:\xxx\xxx\xxx\12.csv -Append -InputObject "KB Patch; $CheckKB"
Out-file C:\xxx\xxx\xxx\12.csv -Append -InputObject "Hotfix $CheckKB was installed on $name by $($HotFixQuery.InstalledBy)"
}
}
}
}
You're outputting raw text into a CSV which is OK but you have to maintain the intended destination format.
out-file is going to output raw text, one per line. That is why your calls are creating a single column. You can change your code to collapse your output to 1 line that is separated by commas:
Out-file C:\Users\xx\Desktop\12.csv -Append -InputObject “computer name: $name,KB Patch: $CheckKB,Hotfix $CheckKB is not installed on $name”
This will write 1 raw line to your CSV file. Your fields will be respected as columns assuming the rest of the CSV file is intact.
I doubt you want to repeat your headers per line but wanted to show you what it would look like. Now you are going to want to write the initial line of the CSV file before you start your loop to output the headers:
Out-file C:\Users\xx\Desktop\12.csv -Append -InputObject “Computer Name,KB Patch,Hotfix”
Then just remove the header stuff from the lines written inside your loop. This does mean that the header will be written with each run so you are either going to want to wipe the file at the start or you are going to want to test if it exists before you write the header.
There are better ways to do this but this doesn't require you to make significant changes to your work. I would create a custom object for each record, create a collection of those objects, and then export them via export-csv. If that would better suite your need please just comment a request for it.

How to check the users currently using a powershell program?

So I have a basic program (incredibly buggy but we quite like it) that uses a shared folder that a couple of people at school have access to (Paths have been changed for ease of use). It is designed to work as a messaging application, with each user writing into the same Notepad file to send a message to a Poweshell script using the Get-Content and -Wait parameter. I have added a couple of commands using "/", but I want one (i.e. /online) that a user can type and see all of the other people currently using the program.
I have tried to set up a different text file that is updated every x seconds by each individual user with their own user name, while wiping the previous record:
while (1){
Clear-Content -Path C:\users\Freddie\Desktop\ConvoOnline.txt
Start-Sleep -Milliseconds 5000
Add-Content -Path C:\users\Freddie\Desktop\ConvoOnline.txt $env:UserName
}
So this can be called upon later:
elseif($_ -match "/online"){Get-Content -Path C:\users\Freddie\Desktop\ConvoOnline.txt}
But this doesn't work, it won't sync up between users, so one user will wipe the current users and only that will apear as active, until the other users' cycle wipes THEIR name.
To avoid the XY Problem, I want a fairly simple way (still only using two files maximum) to determine which users are actively using (therefore updating) the Powershell script they are running.
Whole code:
Add-Type -AssemblyName System.speech
$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer
$speak.Volume = 100
Write-Host "Type /helpp, save it, then hit backspace and save it again for a guide and list of commands!"
Get-Content -Path C:\users\Freddie\Desktop\Convo.txt -Wait |
%{$_ -replace "^", "$env:UserName "} |
%{if($_ -match "/cls"){cls} `
elseif($_ -match "/online"){Get-Content -Path C:\users\Freddie\Desktop \ConvoOnline.txt} `
elseif(($_ -match "/afk") -and ($env:UserName -eq "Freddie")){Write-Host "$env:UserName has gone afk"} `
elseif(($_ -match "/say") -and ($env:UserName -eq "Freddie")) {$speak.Speak($_.Substring(($_.length)-10))} `
elseif($_ -match "/whisper"){
$array = #($_ -split "\s+")
if($array[2] -eq "$env:UserName"){
Write-Host $array[2]
} `
} `
elseif($_ -match "/help"){
Write-Host "Help: `
1. Press Ctrl+S in Notepad to send your message `
2. Make sure you delete it after it's been sent `
3. If your message doesn't send properly, just hit backspace and all but the last letter will be sent `
`
COMMANDS: `
`
/online - Lists all users currently in the chat `
/cls - Clears you screen of all current and previous messages `
/whisper [USERNAME] [MESSAGE] - This allows you to send a message privately to a user"
}
else{Write-Host "$_"}}
#
#
#
#
#Add a command: elseif($_ -match "/[COMMAND]"){[FUNCTION]}
#
#Make it user-specific: elseif($_ -match "/[COMMAND]" -and $envUserName -eq "[USERNAME]"){[FUNCTION]}
You can add time stamp with add-content and another ps1 file for clearing data written before 5 seconds (you can do this in the same ps1 file but another ps1 file is better)
Modified user online updation part :
while ($true){
Add-Content -Path d:\ConvoOnline.txt "$($env:UserName);$(get-date)"
Start-Sleep -Milliseconds 5000
}
Another script which watches and clears content before 5 seconds ,so the online file is always updated
while($true){
Start-Sleep -Milliseconds 5000
$data = get-content -Path D:\ConvoOnline.txt
clear-content -Path D:\ConvoOnline.txt
if($data){
$data | %{if(!([datetime]($_.split(";")[1]) -lt (get-date).addmilliseconds(-4500))){Add-Content -Path d:\ConvoOnline.txt $_}}
}
}

Save powershell script after entering details for read-host

Example:
$nameofpath = read-host "enter path"
get-services | export-csv "$nameofpath"
I want a script so after entering a path such as c:\files\test.txt in the example above, it will save a script with:
get-services | export-csv "c:\files\test.txt"
... so I could go to that file click it and it will run.
At the moment I have an draft script like this but if I know how to do the first example I should hopefully be able to do the same for that
You'd either need to change the script that you're running, or query some other text file. If there is anything in the text file, use that; otherwise, prompt for the value. Here's an example of how you could change the script you're running using the $PSCommandPath (it's an automatic variable that contains the full path and file name of the script that is being run) variable:
$Foo = $null
#If $Foo is $null prompt for value then write that value to the script.
if($Foo -eq $null){
$Foo = Read-Host "Foo"
#Need to becarful not to match this line
$NewScript = Get-Content $PSCommandPath | ForEach-Object {$_ -replace '^\$Foo = \$null$',"`$Foo = '$Foo'"}
$NewScript | Out-File $PSCommandPath -Force
}
Write-Host "Foo $Foo"
Hope this helps.
This looks amateur but works as expected
$path = [Microsoft.VisualBasic.Interaction]::InputBox("Enter path")
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
get-service | export-csv -path $path\services.csv # original command
$Command="get-service | export-csv -path $path\services.csv" # same copy here to save
$command |Out-File "D:\exportservice.ps1" # where you want the file to be saved to run later

Pipe all Write-Output to the same Out-File in PowerShell

As the title suggests, how do you make it so all of the Write-Outputs - no matter where they appear - automatically append to your defined log file? That way the script will be nicer to read and it removes a tiny bit of work!
Little example below, id like to see none of the "| Out-File" if possible, yet have them still output to that file!
$Author = 'Max'
$Time = Get-Date -Format "HH:mm:ss.fff"
$Title = "Illegal Software Removal"
$LogName = "Illegal_Remove_$($env:COMPUTERNAME).log"
$Log = "C:\Windows\Logs\Software" + "\" + $LogName
$RemoteLog = "\\Server\Adobe Illegal Software Removal"
Set-PSBreakpoint -Variable Time -Mode Read -Action { $global:Time = Get-Date -format "HH:mm:ss.fff" } | Out-Null
If((Test-Path $Log) -eq $False){ New-Item $Log -ItemType "File" -Force | Out-Null }
Else { $Null }
"[$Time][Startup] $Title : Created by $Author" | Out-File $Log -Append
"[$Time][Startup] Configuring initial variables required before run..." | Out-File $Log -Append
EDIT: This needs to work on PS v2.0, I don't want the output to appear on screen at all only in the log. So I have the same functionality, but the script would look like so...
"[$Time][Startup] $Title : Created by $Author"
"[$Time][Startup] Configuring initial variables required before run..."
You have two options, one is to do the redirection at the point the script is invoked e.g.:
PowerShell.exe -Command "& {c:\myscript.ps1}" > c:\myscript.log
Or you can use the Start-Transcript command to record everything (except exe output) the shell sees. After the script is done call Stop-Transcript.

Powershell Pass "Press any key to continue" from another script

I'm writing a powershell script to automate some updates.
For this purpose I need to execute another script and save the output into a variable.
Afterwards I can cut the things that I need off and save them into other variables.
These things theoreticly work but the other script which I'm executing stops the process,
beause it need any key to continue at the end.
Does somebody know how I can pass this?
The scripts stops after:
$list = .\list.cmd
Kind regards :)
Thats a part of the script:
Write-Host "Importing..."
cd "$path"
$list = .\list.cmd
Write-Host "Searching for the certificate file"
$CertificateFile = $list | where {$_ -match "Certificate File:"}
$CertificateFile = $CertificateFile.Substring(18)
Write-Host "I'm trying to find the Password File:"
$PasswordFile = $PasswordFile = $list | where {$_ -match "Password File:"}
$PasswordFile.Substring(15)
Write-Host "Searching for the password file"
$Enddate = $list | where {$_ -match "Validity NotAfter:"}
$Enddate = $Enddate.Substring(19)
Here is how you send a keystroke. As for timing, it needs to occur when the paused app is in the foreground 'active' window.
add-type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("A")