Powershell - Fix Line Break - powershell

I have a situation here and I would like to share it with you to ask for help.
I have a TXT file that I receive every day and I need to import it into my ERP, however, this file comes with a line break that we have to manually adjust
And when adjusted, it looks like this:
{
Write-Error "Informe um arquivo para leitura"
Return
}
$arquivo = $args[0]
if(![System.IO.File]::Exists($arquivo))
{
Write-Error "Arquivo nao encontrado: $arquivo"
Return
}
$tamanhoEsperado = 240
$ultimoTamanho = 0
foreach($linha in [System.IO.File]::ReadLines($arquivo))
{
if ($linha.length -gt 0)
{
if (!($linha.length -eq $tamanhoEsperado) -And (($linha.length + $ultimoTamanho) -eq $tamanhoEsperado))
{
Write-Host -NoNewLine $linha
$ultimoTamanho += $linha.length
}
else
{
if ($ultimoTamanho -gt 0)
{
Write-Host
}
Write-Host -NoNewLine $linha
$ultimoTamanho = $linha.length
}
}
else
{
Write-Host
}
}
But I am not able to make the process automatic with this script.
Powershell will look for the TXT file in a specific folder, validate if the file has 240 positions and if not, correct that line break shown in img1. Would that be possible?

Note:
Write-Host is typically the wrong tool to use, unless the intent is to write to the display only, bypassing the success output stream and with it the ability to send output to other commands, capture it in a variable, or redirect it to a file. To output a value, use it by itself; e.g., $value instead of Write-Host $value (or use Write-Output $value). See this answer for more information.
Therefore, your code only produced for-display output, not data.
Try something like the following:
$fragment = ''
$correctedLines =
foreach ($line in [System.IO.File]::ReadLines($arquivo)) {
if ($fragment) { # The previous line was incomplete.
$fragment + $line # Concatenate the fragment with the current line and output.
$fragment = '' # Reset the fragment variable.
} elseif ($line.Length -ne 240) {
$fragment = $line # Save the fragment and continue.
} else {
$line # Complete line -> output it.
}
}
Note the use of implicit output (e.g., $line) and that you can directly collect all output from the foreach statement in a variable.

Related

How to fix line breaks?

I'm kind of new to PowerShell and I need to fix a bank statement file. The problem is that this file has a CNAB 240 standard, however, it comes with a line break, so we have to fix it manually.
I have a script that makes this correction, however, I can't make it run alone because I need to use CMD to run PowerShell.
My idea is to use this script in the task manager to look for the .txt file and fix it, does anyone have any ideas?
Follow the script:
if ($args.count -eq 0)
{
Write-Error "Please enter a file to read"
return
}
$file = $args[0]
if(![System.IO.File]::Exists($file))
{
Write-Error "File not found: $file"
return
}
$expectedsize = 240
$lastSize = 0
foreach($line in [System.IO.File]::ReadLines($file))
{
if ($line.length -gt 0)
{
if (!($row.length -eq $expectedsize) -And (($row.length + $lastSize) -eq $expectedsize))
{
Write-Host -NoNewLine $linha
$lastSize += $line.length
}
else
{
if ($lastSize -gt 0)
{
Write-Host
}
Write-Host -NoNewLine $linha
$lastSize = $line.length
}
}
else
{
Write-Host
}
}
The script above makes the normal correction of the file, however, I am not able to make it follow the steps below:
Browse the txt file
Validate that it does not have the 240 positions in each line
And then make the correction

How can I allow only Y/N to be input in Read-Host without having to press Enter in Powershell?

I'm trying to find a way to have something like a Read-Host to ask the user if they want to output to the file listed or not. With this I want them to either press y or n and then the code continues rather than then pressing y/n then pressing enter as well. At the moment this all works well but again it's not quite what I'm wanting.
I've tried looking into Readkey and SendKeys (to push Enter for the user) but neither work as they seem to only execute after the user has pushed Enter on the Read-Host. I'm still very new to Powershell so I'm not entirely sure whether it's actually possible or not and I've spent too much time googling/testing to find an answer that works. If I was to use Write-Host or something to do this, it needs to not show up in the log.
I've included the necessary part of my script below. It basically asks the user if the file location is correct. If it is they press y and it uses it for the output, otherwise if they push n then it loads the FolderBrowserDialog for them to select the folder they want.
I should also note this is all within a Tee-object as this code is what determines where the Tee-object output goes to.
$OutputYN = Read-Host "Do you want the output file generated to $startDirectory\FolderList.txt? (Y/N)"
If (“y”,”n” -notcontains $OutputYN) {
Do {
$OutputYN = Read-Host "Please input either a 'Y' for yes or a 'N' for no"
} While (“y”,”n” -notcontains $OutputYN)
}
if ($OutputYN -eq "Y") {
$OutputLoc = $startDirectory
}
elseif ($OutputYN -eq "N") {
$OutputLocDir = New-Object System.Windows.Forms.FolderBrowserDialog
$OutputLocDir.Description = "Select a folder for the output"
$OutputLocDir.SelectedPath = "$StartDirectory"
if ($OutputLocDir.ShowDialog() -eq "OK") {
$OutputLoc = $OutputLocDir.SelectedPath
$OutputLoc = $OutputLoc.TrimEnd('\')
}
}
EDIT:
I should have been a little more clear. I had already tried message box type stuff as well but I'd really prefer if there is a way that the user types in a y or a n. I'm not really interested in a popup box that the user has to click. If it's not possible then so be it.
Readkey is the right way.
Use the following as template.
:prompt while ($true) {
switch ([console]::ReadKey($true).Key) {
{ $_ -eq [System.ConsoleKey]::Y } { break prompt }
{ $_ -eq [System.ConsoleKey]::N } { return }
default { Write-Error "Only 'Y' or 'N' allowed!" }
}
}
write-host 'do it' -ForegroundColor Green
:prompt gives the outer loop (while) a name which can be used in the switch statement to directly break out entirely via break prompt (and not within the switch statement).
Alternative (for Windows):
Use a MessageBox.
Add-Type -AssemblyName PresentationFramework
$messageBoxResult = [System.Windows.MessageBox]::Show("Do you want the output file generated to $startDirectory\FolderList.txt?" , 'Question' , [System.Windows.MessageBoxButton]::YesNo , [System.Windows.MessageBoxImage]::Question)
switch ($messageBoxResult) {
{ $_ -eq [System.Windows.MessageBoxResult]::Yes } {
'do this'
break
}
{ $_ -eq [System.Windows.MessageBoxResult]::No } {
'do that'
break
}
default {
# stop
return # or EXIT
}
}
Not sure if this is possible in the console. But when I need the user to write one answer of a specified set, I use a do-until-loop like:
Do {
$a = Read-Host "Y / N"
} until ( 'y', 'n' - contains $a )
try this:
$title = 'Question'
$question = 'Do you want the output file generated to $startDirectory\FolderList.txt?'
$choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes'))
$choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No'))
$decision = $Host.UI.PromptForChoice($title, $question, $choices, 1)
if ($decision -eq 0) {
Write-Host 'Yes'
} else {
Write-Host 'No'
}
If you are on Windows, you can do it :
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
$result = [System.Windows.Forms.MessageBox]::Show('Do you want the output file generated to $startDirectory\FolderList.txt?' , "Question" , [System.Windows.Forms.MessageBoxButtons]::YesNo, [System.Windows.Forms.MessageBoxIcon]::Question)
if ($result -eq 'Yes') {
"Yes"
}
else
{
"No"
}

How can I increase the maximum number of characters read by Read-Host?

I need to get a very long string input (around 9,000 characters), but Read-Host will truncate after around 8,000 characters. How can I extend this limit?
The following are possible workarounds.
Workaround 1 has the advantage that it will work with PowerShell background jobs that require keyboard input. Note that if you are trying to paste clipboard content containing new lines, Read-HostLine will only read the first line, but Read-Host has this same behavior.
Workaround 1:
<#
.SYNOPSIS
Read a line of input from the host.
.DESCRIPTION
Read a line of input from the host.
.EXAMPLE
$s = Read-HostLine -prompt "Enter something"
.NOTES
Read-Host has a limitation of 1022 characters.
This approach is safe to use with background jobs that require input.
If pasting content with embedded newlines, only the first line will be read.
A downside to the ReadKey approach is that it is not possible to easily edit the input string before pressing Enter as with Read-Host.
#>
function Read-HostLine ($prompt = $null) {
if ($prompt) {
"${prompt}: " | Write-Host
}
$str = ""
while ($true) {
$key = $host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown");
# Paste the clipboard on CTRL-V
if (($key.VirtualKeyCode -eq 0x56) -and # 0x56 is V
(([int]$key.ControlKeyState -band [System.Management.Automation.Host.ControlKeyStates]::LeftCtrlPressed) -or
([int]$key.ControlKeyState -band [System.Management.Automation.Host.ControlKeyStates]::RightCtrlPressed))) {
$clipboard = Get-Clipboard
$str += $clipboard
Write-Host $clipboard -NoNewline
continue
}
elseif ($key.VirtualKeyCode -eq 0x08) { # 0x08 is Backspace
if ($str.Length -gt 0) {
$str = $str.Substring(0, $str.Length - 1)
Write-Host "`b `b" -NoNewline
}
}
elseif ($key.VirtualKeyCode -eq 13) { # 13 is Enter
Write-Host
break
}
elseif ($key.Character -ne 0) {
$str += $key.Character
Write-Host $key.Character -NoNewline
}
}
return $str
}
Workaround 2:
$maxLength = 65536
[System.Console]::SetIn([System.IO.StreamReader]::new([System.Console]::OpenStandardInput($maxLength), [System.Console]::InputEncoding, $false, $maxLength))
$s = [System.Console]::ReadLine()
Workaround 3:
function Read-Line($maxLength = 65536) {
$str = ""
$inputStream = [System.Console]::OpenStandardInput($maxLength);
$bytes = [byte[]]::new($maxLength);
while ($true) {
$len = $inputStream.Read($bytes, 0, $maxLength);
$str += [string]::new($bytes, 0, $len)
if ($str.EndsWith("`r`n")) {
$str = $str.Substring(0, $str.Length - 2)
return $str
}
}
}
$s = Read-Line
More discussion here:
Console.ReadLine() max length?
Why does Console.Readline() have a limit on the length of text it allows?
https://github.com/PowerShell/PowerShell/issues/16555

How do I do a conditional replace in PowerShell?

Powershell Newbie looking for some help.
I've got a powershell script which changes a priority value inside a text file to have a value of 50:
if ($line.contains("Priority") )
{
$LastComma = $line.LastIndexOf(",") +1
$N = $line.Substring(0,$LastComma)
$N = $N + "50"
$lines[$counter] = $N
}
This works fine and does exactly what I want it to, but I now need to modify it so it changes the priority value to 45 if there is also the following line present:
Provider = XYZ
If this Provider value is not XYZ then all Priority values are set to 50 as before. Anyone have any advice on how can I achieve this?
Thanks
This example illustrates using Powershell with several concepts:
Matching each line using regular expressions
Replacing each line using regular expressions
Using a switch statement
Here's a test file:
Set-Content test.txt "Provider=ABC,Priority=3
Provider=DEF,Priority=4
Not a provider
Provider=XYZ,Priority=5"
You can transform the test file line by line using the
switch statement (thanks #TheIncorrigible1)
switch -Regex -File test.txt
{
'Provider=XYZ.+Priority='
{
$_ -replace "Priority=\d+","Priority=45"
continue
}
'Priority='
{
$_ -replace "Priority=\d+","Priority=50"
continue
}
default
{
$_
continue
}
}
Please make the effort to provide a working example next time.
Without any more information than what was provided, here is a draft of a solution:
foreach ($line in $lines) {
$value = "XYZ"
$check = $false
if ($line.contains("Provider = $value")) {
$check = $true
}
if ($line.contains("Priority")) {
if ($check) {
$priority = 45
} else {
$priority = 50
}
$LastComma = $line.LastIndexOf(",") +1
$N = $line.Substring(0,$LastComma)
$N = $N + "$priority"
$lines[$counter] = $N
}
}
Remark: This assume that the Provider tag will be found prior to the Priority tags.

Powershell scripting for url custom monitoring

I am trying to build a custom script for URL monitoring. I am able to run the URL's from the file and enter the same in a logfile(named with time stamp).
Till here I have completed
Issue is when I compare the values from present(present timestamp) and previous logfile(previous timestamp).
This portion is not working fine. Please help me correct it.
Here is my code trying to compare value line by line from present logfile and previous logfile and run commands to generate output:
# New log is new logfile data
$Newlog = Get-Content $URLlogfile
$old_file = Dir C:\Scripts\logs | Sort CreationTime -Descending | Select Name -last 1
# Old log is Old logfile data
$oldlog = Get-Content $old_file -ErrorAction SilentlyContinue
Foreach($logdata in $Newlog) {
$url = ($logdata.Split(" "))[0]
$nodename = ($logdata.Split(" "))[1]
$statuscheck = ($logdata.Split(" "))[2]
$description = ($logdata.Split(" "))[3]
$statuscode = ($logdata.Split(" "))[4]
Foreach($log1data in $oldlog) {
$url1 = ($log1data.Split(" "))[0]
$nodename1 = ($log1data.Split(" "))[1]
$statuscheck1 = ($log1data.Split(" "))[2]
$description1 = ($log1data.Split(" "))[3]
$statuscode1 = ($log1data.Split(" "))[4]
While ($url = $url1) {
if ($statuscheck = $statuscheck1 ) {
write-output "output is same"
} elseif ($statuscheck = Fail) {
While ($statuscheck1 = Pass) {
write-output "$url is down at $nodename1- testing event sent"
}
} elseif ($statuscheck = Pass) {
While ($statuscheck1 = Fail) {
write-output "$url is up at $nodename1- testing event sent"
}
}
}
Break
}
}
#At end am clearing the old logs except present one
dir C:\Scripts\logs -recurse | where { ((get-date)-$_.creationTime).minutes -gt 3 } | remove-item -force
Per the comment from BenH, the following part of your code needs correcting as follows:
If ($url -eq $url1) {
if ($statuscheck -eq $statuscheck1 ) {
write-output "output is same"
} elseif ($statuscheck -eq 'Fail' -and $statuscheck1 -eq 'Pass') {
write-output "$url is down at $nodename1- testing event sent"
} elseif ($statuscheck -eq 'Pass' -and $statuscheck1 -eq 'Fail') {
write-output "$url is up at $nodename1- testing event sent"
}
}
Corrections:
In your comparison statements the = needs to be -eq. In PowerShell = always assigns a value.
In your comparison statements Pass and Fail need to be surrounded by single quotes so they are treated as strings (otherwise they are treated like function statements, for functions which don't exist).
I've replaced the While statements with If statements. I'm not sure what the intent of those was but I think they'd just get stuck in an infinite loop as the variable they test is never changed from within the loop.