Trouble reading last line of CSV - powershell

I am getting CSV files (with no header) from another system. The last line ends the file, (there is not a newline after the last line of data). When I try Import-CSV, it will not read the last line of the file.
I do not have the ability to have the input file changed to include the newline.
I have noticed that the Get-Content doesn't have a problem reading the entire file, but then it isn't a CSV and I'm unable to reference the fields in the file.
Currently I'm doing:
$w = Import-CSV -path c:\temp\input.txt -header 'head1', 'head2', 'head3'
This will not read the last line of the file
This reads the entire file:
$w = Get-Content -path c:\temp\input.txt
But the data doesn't have the ability to reference the fields like: $w.head1
Is there a way to get Import-CSV to read the file including the last line?
OR Is there a way to read in the data using Get-Content, adding a header to it and then converting it back to a CSV?
I've tried use ConvertTo-CSV but have not had success:
$w = Get-Content -path c:\temp\input.txt
$csvdata = $w | ConvertTo-CSV # No header option for this function
I'd rather not create an intermediate file unless absolutely necessary.

You're very close! What you're after is not ConvertTo-Csv, you already have the file contents in CSV-format after all. So change that to ConvertFrom-Csv instead, which incidentally does support the -Headers parameter. So something like this:
$w = Get-Content -path c:\temp\input.txt
$csvdata = $w | ConvertFrom-Csv -Header 'head1', 'head2', 'head3'

If I understand correctly, you know the number of columns in the file and all it is missing is a header line. Since in your code you do not specify a -Delimiter parameter I'm assuming the delimiter character used in the file is a comma.
Best thing to do IMHO is to create a new output file and always keep the original.
$fileIn = 'c:\temp\input.txt'
$fileOut = 'c:\temp\input.csv'
# write the header line to a new file
Set-Content -Path $fileOut -Value 'head1,head2,head3'
# read the original file and append it to the one you have just created
Get-Content -Path $fileIn -Raw | Add-Content -Path $fileOut
If your file is really large, below a faster alternative:
$fileIn = 'c:\temp\input.txt'
$fileOut = 'c:\temp\input.csv'
# write the header line to a new file
Set-Content -Path $fileOut -Value 'head1,head2,head3'
# read the original file and append it to the one you have just created
[System.IO.File]::AppendAllText($fileOut, ([System.IO.File]::ReadAllText($fileIn)))
If you really do want to take the risk and overwrite the original file, you can do this:
$file = 'c:\temp\input.txt'
$content = Get-Content -Path $fileIn -Raw
# write the header line to a the file destroying what was in there
Set-Content -Path $file -Value 'head1,head2,head3'
# append the original content to it
$content | Add-Content -Path $file

Related

How to get the heading like this using powershell

I want to get the heading as "StudentID|studentfirstname|studentlastname|class" to my existing data as below:
2|vicky|kash|A
5|abc|sdf|B
9|sdf|sdf|D
My code looks like:
add-content -path "outfile.txt" -Value (-join($StudentID, "|",`
$studentfirstname, "|",` $studentlastname, "|",`$class)
Expected output file:
StudentID|studentfirstname|studentlastname|class
2|vicky|kash|A
5|abc|sdf|B
9|sdf|sdf|D
Thanks in Advance!
Although I'm not quite sure what you intend to do, but to me the question reads as "I have pipe-delimited data and all it is missing is a header line".
If that is the case, you coud do something as simple as:
$fileIn = 'D:\Test\YourFile.csv'
$fileOut = 'D:\Test\YourFile2.csv'
# write the header line to a new file
Set-Content -Path $fileOut -Value "StudentID|studentfirstname|studentlastname|class"
# read the original file and append it to the one you have just created
Get-Content -Path $fileIn -Raw | Add-Content -Path $fileOut
If your input file is really large, below a faster alternative:
$fileIn = 'D:\Test\YourFile.csv'
$fileOut = 'D:\Test\YourFile2.csv'
# write the header line to a new file
Set-Content -Path $fileOut -Value "StudentID|studentfirstname|studentlastname|class"
# read the original file and append it to the one you have just created
[System.IO.File]::AppendAllText($fileOut, ([System.IO.File]::ReadAllText($fileIn)))
That syntax is incorrect...
Just do this...
$StudentID = '123'
$studentfirstname = 'John'
$studentlastname = 'Doe'
$class = 'Math'
Clear-Host
"$StudentID|$studentfirstname|$studentlastname|$class"
# Results
<#
123|John|Doe|Math
#>
Or
Clear-Host
$StudentID,$studentfirstname,$studentlastname,$class -join '|'
# Results
<#
123|John|Doe|Math
#>
Or
Clear-Host
"{0}|{1}|{2}|{3}" -f $StudentID,$studentfirstname,$studentlastname,$class
# Results
<#
123|John|Doe|Math
#>

Preserve Emojis with Get-Content Powershell

I want to preserve Emojis with Get-Content.
When I pull the string from the feed I get the following result:
$WebResponse = Invoke-RestMethod $website
$str_outputNAME = $feed.title
Wanna try😉?
But when I save the content of the file and append it after I have the following result:
$content = (Get-Content -Path $file) -join "`n"
$toWrite = $top_line+$toWrite+$content
$toWrite | Out-File -FilePath $file;
Wanna try???
Background-Info
I want to use Powershell to read a rss-feed.
Therefor I need to append a string at the start of my CSV-File on update.
Because my question was regarding *.csv files I found that a better way is too use
$content = Import-Csv -Path $file
instead of
$content = Get-Content -Path $file
Now all my Emojis are preserved within the file but the processing of the script takes twice the time now.
I tried all different possible Get-Content -Encoding arguments but without luck.
Always resulted in loss towards the formatting of emojis.

Saving Find and Replace text function for .dat files in Powershell

I'm making a script that will find and replace all the instances of a word with another. However I'm unsure how to save the changes.
$file = Get-Content "C:\Script.dat" -Raw
$old = 'oldword'
$new = 'newword'
$file.Replace($old,$new)
Initially I had used the following but this caused issues.
$file.Replace($old,$new) | Set-Content $file
This caused the issue the error of
Set-Content : Cannot find drive. A drive with the same *some random stuff*...
How would I be able to save the changes and/or fix the above issue?
$file = Get-Content "C:\Script.dat" -Raw
$old = 'oldword'
$new = 'newword'
$file.Replace($old,$new) | Out-File -FilePath C:\Script.dat
You were very close, but Set-Content needs two things: a path to the file location and the value to store. Personally, I prefer to overwrite variables when using the .Replace() method instead of piping it into other cmdlets.
This will do it:
$file = Get-Content "C:\Script.dat" -Raw
$old = 'oldword'
$new = 'newword'
$file = $file.Replace($old,$new)
Set-Content -Path "C:\Script.dat" -Value $file
If possible, try to avoid storing files directly at C:\ since that often needs admin rights to write to.
Additionally, you could pipe to Set-Content in a similar way originally listed but you still need to give it the path to the file:
$file.Replace($old,$new) | Set-Content "C:\Script.dat"

Powershell ExportCSV exports an empty file when the imported file comes from Import-csv

I have a CSV file where I need to change the delimiter from comma to semicolon.
I fixed this issue with following command
(Import-CSV $File) | Export-Csv $File -delimiter ';' -notypeinformation
But when the imported CSV file contains only one line which I assume is being handled as the headers. It deletes this line in the output file and it becomes an empty file.
However when I have more than one line and the csv contains the header and some data the exported file is being executed correctly with the headers and the data.
Code that I used to set the header file in the beginning of my script
#set csv headers
Set-Content -Path "$FolderNameCiTypeDelta" -Value "name,type,location,size"
I've tried adding the code
(Import-CSV $File -Header "name,type,location,size") | Export-Csv $File -delimiter ';' -notypeinformation
But this made just an extra line with the headers I specified.
[EDIT]
The 2 csv files that I test with contain the following lines
The file that becomes empty with the import --> export command contains
name,type,location,size
The file that performs what I need with the import --> export command contains
name,type,location,size
test,extra,line
You can do the following:
$file = 't.txt'
$contents = Get-Content $file -totalcount 2
if ($contents.Count -gt 1) {
(Import-Csv $file) | Export-Csv $file -Delimiter ';' -NoType
}
if ($contents.Count -eq 1) {
$contents -replace ',',';' | Set-Content $file
}
The idea here is to read two lines of your file. If it only contains one line, it will use -replace to fix your delimiters. If it has more than two lines, it will use the *-Csv commands.
The -replace section only works if you don't have commas in your field values. If you do have commas in your fields, then you should have text qualifiers (double quotes most likely) around your fields. In that case, you can simply use -replace '","','";"'.

Change value on specific lines of multiple files

I am testing software which has settings in text files.
Now i need to change a specific line in ~100 files.
I searched hours for it and i am close to a solution. But dont know how to get it done.
A solution in notepad++ would be nice, but i tried it with powershell with the following command:
# File to change
$file = *.dat
# Get file content and store it into $content variable
$content = Get-Content -Path $file
# Replace the line number 40 with "0"
$content[39] = '"0"'
# Set the new content
$content | Set-Content -Path $file
It changes the specific line, but it also adds the data of all the files, in all the files in the folder. So in case of 200 lines the files now have 20000 lines. Every file.
I want to change in all the files linenumber 40:
"0"
change to
"1"
Because there are multiple values with "0" on other lines, i only want to change line 40 in multiple files.
You probably have to iterate over these files. Example:
Get-ChildItem *.dat | ForEach-Object {
$content = Get-Content -Path $_
$content[39] = '"0"'
$content | Set-Content -Path $_
}