I am fighting with outputting text file and getting file content in exactly the same way as it was provided by the user. I tried to find an answer but the only thing I can find is how to change multiline into one string which is quite the opposite I need.
How to ensure that input provided by the user in multiline will not be altered to single line in PowerShell?
Details:
In inputbox I provide details in multiline:
PowerShell saves the output in one line:
I want PowerShell to save multiline input and then show multiline output.
Here is the code:
function Notes
{
$var = $Users.Text
$var | Out-File C:\Users\A570654\Desktop\users.txt
$var = Get-Content C:\Users\A570654\Desktop\users.txt
Set-Content -Path C:\Users\A570654\Desktop\notes.txt "*************************************************
$var
*************************************************"
Invoke-Item -Path C:\Users\A570654\Desktop\notes.txt
}
############################## FORMS #######################################
$Users = New-Object System.Windows.Forms.RichTextBox
$Users.Size = New-Object System.Drawing.size(470,85)
$Users.Location = New-Object System.Drawing.Size(10,100)
$Users.MultiLine = $true
$Users.AutoSize = $true
$Users.ScrollBars = "Vertical"
$users.AcceptsTab
$form = New-Object system.windows.forms.form
$form.size = New-Object system.drawing.size(600,600)
$button = New-Object system.windows.forms.button
$button.text = "Get Notes"
$button.Add_Click({Notes})
$form.Controls.Add($Users)
$form.Controls.Add($button)
$form.Showdialog()
The reason why I decided to save the string in file is that output shows spaces as in picture 2. If I decide not to save output to file and then get its content then string appears in the following format: JohnTomKate.
You are saving the user input to a file and reload the content which will give you the current output. So just use the variable without reassigning it:
$var = $Users.Text
$var | Out-File C:\Users\A570654\Desktop\users.txt
Set-Content -Path C:\Users\A570654\Desktop\notes.txt "*************************************************
$var
*************************************************"
Output:
*************************************************
John
Tom
Kate
*************************************************
Related
I'm trying to create a Powershell Script, which will search specified folder and list all subfolders into a listbox. When the user selects a folder from the listbox, an action is performed.
I'm fairly new to powershell so I don't know if I'm on the right track but I've tried putting the directory into an array, I just have no idea how to get the array into the listbox.
$items = Get-ChildItem -Path $path
foreach ($item in $items)
{
# if the item is a directory, then process it.
if ($item.Attributes -eq "Directory")
{
Write-Host $item.Name
}
}
For demo, here's a simple form that fills a listbox with subfolder paths.
$rootFolder = 'PATH TO YOUR ROOTFOLDER HERE'
# get an array of subfolder full names in the $rootFolder
$subfolders = (Get-ChildItem -Path $rootFolder -Recurse -Directory).FullName
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form
$form.Text = "SubFolders"
$form.Size = New-Object System.Drawing.Size(300,300)
$form.StartPosition = "CenterScreen"
$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Location = New-Object System.Drawing.Point(10,40)
$listBox.Size = New-Object System.Drawing.Size(260,180)
$listBox.Anchor = 'Top,Right,Bottom,Left'
# fill the listbox with subfolder names
$listBox.items.AddRange($subfolders)
# add an event handler on the listbox to do something with the selected item
$listBox.Add_Click({
# here put your code to perform some action with the selected subfolder
$selected = $listBox.GetItemText($listBox.SelectedItem)
# for demo, simply show a messagebox
[System.Windows.Forms.MessageBox]::Show("You selected subfolder`r`n`r`n$selected", "Subfolder")
})
$form.Controls.Add($listBox)
$form.ShowDialog()
$form.Dispose()
Update
As per your comment, you would like to add a second column to the listbox where the LastWriteTime (lastModified) date is shown.
The earlier code can be adapted quite easily to add string items that are a combination of the folder FullNames, combined with the LastWriteTime using some sort of separation character. Later on, split the selected item out on that character to get the foldername only.
However, for user experience, this would make a mess of the whole thing..
Adding a new column would be nicer, but although the Listbox object does have a MultiColumn property, the result of using that is most likely NOT what you expect..
Below the updated code for using multiple columns in a ListView object instead of a ListBox.
$rootFolder = 'PATH TO YOUR ROOTFOLDER HERE'
# get an array of subfolder objects in the $rootFolder
$subfolders = Get-ChildItem -Path $rootFolder -Recurse -Directory
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form
$form.Text = "SubFolders"
$form.Size = New-Object System.Drawing.Size(600,400)
$form.StartPosition = "CenterScreen"
$listView = New-Object System.Windows.Forms.ListView
$listView.Location = New-Object System.Drawing.Point(10,40)
$listView.Size = New-Object System.Drawing.Size(560,280)
$listView.Anchor = 'Top,Right,Bottom,Left'
$listView.View = 'Details'
$listView.FullRowSelect = $true
$listView.GridLines = $true
[void]$listView.Columns.Add("Folder", 338);
[void]$listView.Columns.Add("LastModified", 200);
# fill the listbox with subfolder names and Last Modified dates
$subfolders | ForEach-Object {
$row = New-Object System.Windows.Forms.ListViewItem( $_.FullName) # the folder path goes into the first column
[void]$row.SubItems.Add($_.LastWriteTime.Tostring()) # the LastWriteTime goes into the second column
[void]$listView.Items.Add($row)
}
# add an event handler on the listbox to do something with the selected item
$listView.Add_Click({
# here put your code to perform some action with the selected subfolder
$selected = $listView.SelectedItems[0].Text
# for demo, simply show a messagebox
[System.Windows.Forms.MessageBox]::Show("You selected subfolder`r`n`r`n$selected", "Subfolder")
})
$form.Controls.Add($listView)
[void]$form.ShowDialog()
$form.Dispose()
Hope that helps
I've got an application that opens a winform and asks the user to input a PDF file. Because I can't read strings in PDF files easily, I need to convert it to a .txt. When the user clicks OK, the application does this.
The problem I'm having is now using the .txt file object and passing it to another command without knowing the name of it. When I try to pipe it to another command, it won't work because I don't have the path. I think this is because the output of conversion is the string "OK" and not the actual .txt file.
How can I convert the PDFs to text (I'm using Xpdf) and pass the converted file down the pipeline for further processing?
If the means I'm using is the problem, how can I accomplish this task another way?
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form
$form.StartPosition = 'CenterScreen'
$button = New-Object System.Windows.Forms.Button
$form.Controls.Add($button)
$button.Text = 'Get file'
$button.Location = '10,10'
$button.Add_Click({
$ofd = New-Object system.windows.forms.Openfiledialog
$ofd.Filter = 'PDFs (*.pdf)|*.pdf'
$script:filename = 'Not found'
if ($ofd.ShowDialog() -eq 'Ok') {
$script:filename = $textbox.Text = $ofd.FileName
}
})
$buttonOK = New-Object System.Windows.Forms.Button
$form.Controls.Add($buttonOK)
$buttonOK.Text = 'Ok'
$buttonOK.Location = '10,40'
$buttonOK.DialogResult = 'OK'
$textbox = New-Object System.Windows.Forms.TextBox
$form.Controls.Add($textbox)
$textbox.Location = '100,10'
$textbox.Width += 50
$form.ShowDialog()
$output = & "C:\Users\eakinsa\Desktop\Style Guide Report\Includes\bin32\pdftotext" $filename
$output |
Get-Location -OutVariable textFile |
Select-String -Path $textFile -Pattern ed
Per Ansgar:
I amended the lines last few lines to, for now, maintain the default functionality of pdftotext where it creates the file in the same directory with the same name, as with his suggestion, I could easily replace .pdf with .txt on the end of the file path, thereby having the flexibility to pass the correct file path to subsequent functions. That made it so I was able to search the text file.
& "C:\users\eakinsa\Desktop\Style Guide Report\Includes\bin32\pdftotext" $filename
$pdf = Get-Item $filename
$textfile = $filename -replace '\.pdf$', '.txt'
Select-String -Path $textfile -Pattern ed
When you run pdftotext with just the input PDF as argument it creates the output text file in the same directory with the same basename and the extension txt.
& pdftotext C:\temp\foo.pdf # creates C:\temp\foo.txt
So you can build the text file path like this:
$pdf = Get-Item $filename
$textfile = Join-Path $pdf.DirectoryName ($pdf.BaseName + '.txt')
or like this:
$textfile = $filename -replace '\.pdf$', '.txt'
Alternatively you can tell pdftotext where to create the output file:
$textfile = 'C:\some\where\bar.txt'
& pdftotext $filename $textfile # creates C:\some\where\bar.txt
I am trying to create a PowerShell script that will send an email if a service goes into a stopped state. I would like to be able to read the email configuration from another file.
Email configuration file:
.\emailconfig.conf
$emailSmtpServer = "smtp.company.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "usera"
$emailSmtpPass = "passwordb"
$emailFrom = "userA#company.com"
$emailTo = "userB#company.com"
$emailcc= "userC#company.com"
And this is what I have so far in the PowerShell script:
.\emailservicecheck.ps1
$A = Get-Service "Service B"
if ($A.Status -eq "Stopped") {
Get-Content emailconfig.conf | Out-String
$emailMessage = New-Object System.Net.Mail.MailMessage($emailFrom, $emailTo)
$emailMessage.Cc.Add($emailcc)
$emailMessage.Subject = "subject"
#$emailMessage.IsBodyHtml = $true # true or false depends
$emailMessage.Body = Get-Service "Service B" | Out-String
$SMTPClient = New-Object System.Net.Mail.SmtpClient($emailSmtpServer, $emailSmtpServerPort)
$SMTPClient.EnableSsl = $False
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailSmtpUser, $emailSmtpPass);
$SMTPClient.Send($emailMessage)
}
The script works if I enter the text from the email config file into the script but I cannot seem to be able to read in the data from the file on the fly and get the script to work. It errors out and says that my variables are empty.
What you are searching for, (I think) are .psd1 files. I personally prefer them (along with JSON) over the other configuration formats. The link I'm referring to also describes other well-known formats and how to use them in PowerShell.
In short, module manifests work as follows:
configuration.psd1
#{
SmtpServer = "";
MailFrom = "";
Auth = #{
User = "";
Pass = "";
};
}
Script.ps1
$mailConfig = Import-LocalizedData -BaseDirectory C:\ -FileName configuration.psd1
$emailMessage = New-Object System.Net.Mail.MailMessage( $$mailConfig.mailFrom , $mailConfig.mailTo )
As Mark already pointed out, Get-Content emailconfig.conf | Out-String will just output the content of the file, it won't define the variables in your code. For that you'd need to dot-source the file, which requires a file with the extension ".ps1".
If you want to stick with a simple config file format I'd recommend changing the file to something like this:
emailSmtpServer = smtp.company.com
emailSmtpServerPort = 587
emailSmtpUser = usera
emailSmtpPass = passwordb
emailFrom = userA#company.com
emailTo = userB#company.com
emailcc = userC#company.com
And importing it into a hashtable via ConvertFrom-StringData:
$cfg = Get-Content emailconfig.conf | Out-String | ConvertFrom-StringData
The data in the hashtable can be accessed via dot-notation ($cfg.emailFrom) as well as via the index operator ($cfg['emailFrom']), so your code would have to look somewhat like this:
$msg = New-Object Net.Mail.MailMessage($cfg.emailFrom, $cfg.emailTo)
$msg.Cc.Add($cfg.emailcc)
$msg.Subject = 'subject'
$msg.Body = Get-Service 'Service B' | Out-String
$smtp = New-Object Net.Mail.SmtpClient($cfg.emailSmtpServer, $cfg.emailSmtpServerPort)
$smtp.EnableSsl = $false
$smtp.Credentials = New-Object Net.NetworkCredential($cfg.emailSmtpUser, $cfg.emailSmtpPass)
$smtp.Send($msg)
It looks like what you're trying to do is include some script from another file. This can be done by dot sourcing, however the file needs to be saved as a .ps1 file, you can't use .conf.
You'd do it as follows (in place of your existing Get-Content) line:
. .\emailconfig.ps1
Assuming the file is kept in the current working directory of the script.
Your script wasn't working because
get-content emailconfig.conf | Out-String
Was returning the contents of that file to the output pipeline, rather than including it in the script and executing it.
I'm not sure i understood correctly what you want.
If you want to use variables from external file, you need to dot source your external script, for example, create a file named variables.ps1 and put in the same folder
In the beginning of the main script use
. .\variables.ps1
If you are after expanding variables that are in external file to ues as an email template please do as following:
$HTMLBody = get-content "yourfilepath" | Foreach-Object {$ExecutionContext.InvokeCommand.ExpandString($_)}
This will expand all variables and put it in the $HTMLBody variable
Then use:
$emailMessage.Body = (ConvertTo-Html -body $HTMLBody)
PFB the code. I have tried to capture the output directly, but no luck. Then I tried to put the output into a log file and later capture the text from log file. That also didn't work. could you please tell me what is wrong in this.
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$Form = New-Object System.Windows.Forms.Form
$Form.width = 600
$Form.height = 600
$Form.Text = "Form Title"
$Form.startposition = "centerscreen"
$Form.FormBorderStyle= [System.Windows.Forms.FormBorderStyle]::Fixed3D
Function ShowProcess(){
$Textbox1 = New-Object System.Windows.Forms.RichTextBox
#$text = Get-Process | Out-String
$Textbox1.Size = New-Object System.Drawing.Size(550, 400)
$Textbox1.Multiline = $true
$Textbox1.Font ="Lucida Console"
$Textbox1.WordWrap = $false
Start-Transcript -Path C:\Mywork\log.txt
&C:\Mywork\Script\test.bat
Stop-Transcript
$Textbox1.Text = Get-Content C:\Mywork\log.txt
$Form.Controls.Add($Textbox1)
}
$button1 = New-Object System.Windows.Forms.Button
$button1.Location = New-Object System.Drawing.Point(350, 450)
$button1.Size = New-Object System.Drawing.Size(120, 100)
$button1.Text = "Press"
$button1.FlatAppearance.BorderSize=0
$Form.Controls.Add($button1)
$button1.Add_Click({ShowProcess})
$Form.ShowDialog()
Below is the content of test.bat:
PS C:\Users\sghosh> Get-Content C:\Mywork\Script\test.bat
tree
pause
Remove pause from your batch file. It prompts the user for confirmation, but since you're running the script non-interactively from your PowerShell code it causes the batch script to hang. Thus no batch output ever gets to the form.
Change test.bat to this:
#echo off
tree
and change these lines in your PowerShell script
Start-Transcript -Path C:\Mywork\log.txt
&C:\Mywork\Script\test.bat
Stop-Transcript
$Textbox1.Text = Get-Content C:\Mywork\log.txt
to this:
$Textbox1.Text = & C:\Mywork\Script\test.bat *>&1 | Out-String
Is it possible using the System.Windows.Forms.TextBox (or any other method) to enter in text that can then be converted to securestring? I would like to be able to take a value entered, convert it to securestring and write that to a file which could then be called on if needed and the securestring converted back should we need to identify the value.
I've looked into something similar to this, but since I am using the TextBox forms, I don't want to rely on Read-Host
$secstr = Read-Host -AsSecureString "Enter your text"
$secstr | ConvertFrom-SecureString | out-file C:\temp\test.txt
$secstr = get-content c:\temp\test.txt | ConvertTo-SecureString -AsPlaintText -Force
Essentially, I want to have a text box use masked/password characters (which I can do with $TextBox.PasswordChar = "*" and then take that input and dump it into a securestring text file. Then, I could use another script to call on that file and display the text in plain text for the end user should they need to know that current value.
Use a MaskedTextBox instead of a regular TextBox if you want to embed this in a custom GUI:
Add-Type -Assembly 'System.Windows.Forms'
$form = New-Object Windows.Forms.Form
$password = New-Object Windows.Forms.MaskedTextBox
$password.PasswordChar = '*'
$password.Top = 100
$password.Left = 80
$form.Controls.Add($password)
$form.ShowDialog()
[source]
and then convert the text to a secure string:
$secstr = $password.Text | ConvertTo-SecureString -AsPlaintText -Force
If you just want to prompt for credentials you could use Get-Credential, which already stores the entered password as a secure string:
PS C:\> $cred = Get-Credential
Cmdlet Get-Credential an der Befehlspipelineposition 1
Geben Sie Werte für die folgenden Parameter an:
Credential
PS C:\> $cred.Password
System.Security.SecureString
An additional way is to use a plain TextBox and as Ansgar mentioned use the PasswordChar attribute:
$textbox = New-Object System.Windows.Forms.Textbox
$textbox.Size = '75,23'
$textbox.PasswordChar = '*'
$form.Controls.Add($textbox)