How to read particular range to last line of file using PowerShell? - powershell

How to read particular range of line ie if my file have 100 lines and I want to read the lines from 80 to last line of file using PowerShell?here I'm not sure about how many lines are available in the file.

Just do it :
Get-Content "C:\temp\test.txt" | select -skip 80

Or just get the last 20 lines:
Get-Content -Path 'C:\temp\test.txt' -Tail 20
If you want to find out how many lines the file has so that you can use that to decide what to read, this could help:
$lines = Get-Content -Path 'C:\temp\test.txt'
$lines.count
So if you decide you want to get for example the last half of the lines you could do something like this:
$lines = Get-Content -Path 'C:\temp\test.txt'
$half = [math]::Round($lines.count/2)
Select-Object -InputObject $lines -Last $half

Related

Powershell remove lines containing the incorrect number of words

Can this be done in easily or at all in powershell?
How would one remove all lines from "test.txt" that do not contain exactly 24 words
This is not too hard in PowerShell.
Something like below should do it:
# read the file and use Where-Object to capture only those lines that have 24 words exactly.
# the regex -split uses '\W+', meaning to split each line on (at least one) Non-Word character.
$result = Get-Content -Path 'D:\test.txt' | Where-Object { ($_ -split '\W+').Count -eq 24 }
# output on screen
$result
# write output to new file
$result | Out-File -FilePath 'D:\test24.txt' -Force

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 $_
}

How to Replace Multiline texts in multiple files using powershell

I am not able to replace multi line texts at all or in other word combining them to one line . here is the example for the file c:\me\testes.text and the contents are as follows
<error.error1>
<error.rec2>
<error.short3>
<error.error4>
<error.rec5>
<error.short6>
My exceptions: I want to combine or replace each first 3 lines to one single line across in the multiple files that is
<error.error1> <error.rec2> <error.short3>
<error.error4> <error.rec5> <error.short6>
I am able to combine each first 2 lines but here its 3 lines .
Please help,
MJ
Use Get-Content to read the file 3 lines at a time by specifying the -ReadCount parameter, pipe these 3 lines to ForEach-Object and join them together.
Get-Content "c:\me\testes.text" -ReadCount 3 | ForEach-Object {
$_ -join ' '
}
Output will look like:
<error.error1> <error.rec2> <error.short3>
<error.error4> <error.rec5> <error.short6>
To read multiple files, 3 lines at a time you can pipe from Get-ChildItem into a loop and then grab the content from the file before joining it back together and writing it back to the respective file.
Get-ChildItem -Path c:\me\testes\* -Filter *txt | ForEach-Object {
(Get-Content -LiteralPath $_.FullName -ReadCount 3) | ForEach-Object {
$_ -join ''
} | Set-Content -LiteralPath $_.FullName
}

How can I count the number of CSV columns when the file has multiline data and no header

My CSV files have no headers and multi line entries like this:
11;"multi line
col12";13;foobar;foobar
21;22;23;24;25
And I'd like to count the number of columns. So 5 in this example. How do I do that?
What I tried:
Import-CSV doesn't work without the header parameter due to duplicate entries on the first line.
(Import-Csv .\bad.csv -Delimiter ";" | get-member -type NoteProperty).count
Adding a header parameter skews the count.
(Import-Csv .\bad.csv -Delimiter ";" -Header (1..99) | get-member -type NoteProperty).count
I had to abort reading the file manually via Get-Content because of all the parsing I would have to handle manually. Escaping characters and multi line entries...
My version of PowerShell is 3 and I have to port my script to version 2 later on.
If you are willing to accept the caveat that this could miscount the number of columns if there are quoted delimiters in string this could be good enough for you.
$path = "c:\temp\test.txt"
$delimiter = ";"
$numberOfColumns = Get-Content $path |
ForEach-Object{($_.split($delimiter)).Count} |
Measure-Object -Maximum |
Select-Object -ExpandProperty Maximum
Import-Csv $path -Header (1..$numberOfColumns) -Delimiter $delimiter
Read in the file with Get-Content and isolate the maximum number of columns by
splitting each line on its delimiter and then using that value to import the CSV. If the file is large you can read in the file once with Get-Content and then use ConvertTo-CSV once you know your column count.
If all lines contain a line break on them the above logic would fail. Still we could temporarily scrub the data by removing the correct line breaks in order to get the accurate count.
$delimiter = ";"
$fileData = (Get-Content $path | Out-String)
$numberOfColumns = ((($fileData -replace "(`"[^;]+?)`r`n",'$1') -split "`r`n" | Select -First 1).split($delimiter)).Count
$fileData | ConvertFrom-Csv -Header (1..$numberOfColumns) -Delimiter $delimiter
What this will do is find lines that end where there is a double quote followed by data that does not contain the delimiter. We also match the newline that follows but drop that same new line in the replacement. If that is done we know that the first line is proper. Use that same line to split and count just like before.
Since Excel knows, let's ask him :
$path = "path\to\bad.csv"
$excel = New-Object -ComObject Excel.Application
$workbook = $excel.Workbooks.Open($path)
$sheet = $workbook.ActiveSheet
$columnIndex = 1
while($sheet.Cells.Item(1, $columnIndex).Text -ne "") {
$columnIndex++
}
"There are $($columnIndex - 1) columns in CSV file $path"
Start-Sleep -Seconds 1
Get-Process excel | Stop-Process -Force
As pointed out by Ansgar Wiechers in comments, there is a much shorter solution :
$path = "path\to\bad.csv"
$excel = New-Object -ComObject Excel.Application
$workbook = $excel.Workbooks.Open($path)
$sheet = $workbook.ActiveSheet
$columnCount = $sheet.UsedRange.Columns.Count
"There are $columnCount columns in CSV file $path"
Start-Sleep -Seconds 1
Get-Process excel | Stop-Process -Force
(I know my way of killing Excel is dirty, but iirc it takes too much code to do so)
I know this is very old, but I came across a similar situation (did not have have rows of varying columns) today and found my own solution so I thought I would share for anyone else coming into this situation. My solution was to use Get-Content for the first row of the CSV and -split on the delimiter (,) to create an array and then return the count of the array. As mentioned in replies above, this will not account for delimiters existing within quotations.
((Get-Content $PathToCsv)[0] -split ",").count
I had the same issue and went with AAgent suggestion.
$CommaCount = ((Get-Content $PathToCsv)[0] -split ",").count
$SemicolonCount = ((Get-Content $PathToCsv)[0] -split ";").count
if ($CommaCount -gt $SemicolonCount){
$CMSlist = Import-Csv ($PathToCsv) –Delimiter “,”
}
else{
$CMSlist = Import-Csv ($PathToCsv) –Delimiter “;”

Powershell .csv merge with column remove

Using the code below I am able to merge several .csv files in 5 seconds.
$getFirstLine = $true
get-childItem "C:\my\dir\*.csv" | foreach {
$filePath = $_
$lines = $lines = Get-Content $filePath
$linesToWrite = switch($getFirstLine) {
$true {$lines}
$false {$lines | Select -Skip 1}
}
$getFirstLine = $false
Add-Content "C:\my\dir\output_code2.csv" $linesToWrite
}
I would like to take this one step further, preferable using piping to remove several of the columns using a command like:
select DateAndTime,DG1_KW,DG2_KW,WT_KW,HTR1_KW,POSS_Load_KW,INV1_KW,INV2_SOC|Export-csv output_test.csv -Notypeinformation
that being the variables in the header of each file.
How would I modify this code to make this work? The idea here is that I am going to be working with hundreds up to thousands of files.
I have other code which can do this but it is no where near as fast.
for instance using 10 .csv files that are 450kb each. the code below takes 20 seconds to process and spits out a .csv file in 20 seconds removing 48 of the 56 columns leaving the variables I need. If I remove part of the code that trims the columns it still takes 12+ seconds.
# Directory containing csv files, include *.*
$directory = "C:\my\dir\*.*";
# Get the csv files
$csvFiles = Get-ChildItem -Path $directory -Filter *.csv;
#$content = $null;
$content = #();
# Process each file
foreach($csv in $csvFiles)
{
$content += Import-Csv $csv;
}
# Write a datetime stamped csv file
$datetime = Get-Date -Format "yyyyMMddhhmmss";
$content |Export-Csv -Path "C:\my\dir\output_code2_$datetime.csv" -NoTypeInformation;
The code I would like to modify runs those same 10 files in 5 seconds but does not remove the 48 columns.
Any Ideas guys?
Ok, you want an example... Let's say your CSVs always look like this:
Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8,Col9,Col10
data1,data2,data3,data4,data5,data6,data7,data8,data9,data10
dataA,dataB,dataC,dataD,dataE,dataF,dataG,dataH,dataI,dataJ
Now let's say you only want Col1, Col2, Col6, Col9, and Col10. You could do a RegEx replace something like:
$Files = get-childItem "C:\my\dir\*.csv" | Select -Expand FullName
ForEach($File in $Files){
If($SkipFirst){
Get-Content $File | Select -Skip 1 | ForEach{$_ -replace "^((?:.*?\,){2})(?:.*\,){3}(.*?\,)(?:(?:.*?\,){2})(.*?,.*?)$", '$1$2$3'} | Add-Content "C:\my\dir\output_code2.csv"
}Else{
Get-Content $File | ForEach{$_ -replace "^((?:.*?\,){2})(?:.*\,){3}(.*?\,)(?:(?:.*?\,){2})(.*?,.*?)$", '$1$2$3'} | Add-Content "C:\my\dir\output_code2.csv"
}
}
That would extract just the columns that I noted above. See https://regex101.com/r/jY4oO6/1 for detailed breakdown of RegEx string. Effective output would be (skipping first line if so dictated):
Col1,Col2,Col6,Col9,Col10
data1,data2,data6,data9,data10
dataA,dataB,dataF,dataI,dataJ