Delete first n lines in file (.zip file with many "strange" symbols) - powershell

This powershell code delete first 4 lines in file
(gc "old.zip" | select -Skip 4) | sc "new.zip"
But old.zip file has Unix (LF) line endings
And this code also converts file line endings to Windows (CR LF)
How to delete first 4 lines without converting ?
Because of the presence of many "strange" symbols in .zip, other ways to remove the first n lines in a .zip file do not work. For example, more than +4 "old.zip"> "new.zip" in cmd does not work, etc.
Through powershell something like it is removed but also not without problems.
Do you know other ways to remove the first n lines in a .zip file ?

somthing like this?
$PathZipFile="c:\temp\File_1.zip"
$NewPathZipFile="c:\temp\NewFile_1.zip"
#create temporary directory name
$TemporyDir=[System.IO.Path]::Combine("c:\temp", [System.IO.Path]::GetRandomFileName())
#extract archive into temporary directory
Expand-Archive "c:\temp\File_1.zip" -DestinationPath $TemporyDir
#loop file for remove 4 first lines for every files and compress in new archive
Get-ChildItem $TemporyDir -file | %{
(Get-Content $_.FullName | select -Skip 4) | Set-Content -Path $_.FullName;
Compress-Archive $_.FullName $NewPathZipFile -Update
}
Remove-Item $TemporyDir -Recurse -Force

PowerShell:
(gc "old.txt" | select -Skip 4 | Out-String) -replace "`r`n", "`n" | Out-File "new.txt"
C#:
File.WriteAllText("new.txt", string.Join("\n", File.ReadLines("old.txt").Skip(4)));

Related

Add eighth and ninth lines to all *.txt files

i have more than 100 txt files in C:\myfolder*.txt
when i run this script from "C:\myfolder" i can add eighth and ninth lines to somename.txt
#echo off
powershell "$f=(Get-Content somename.txt);$f[8]='heretext1';$f | set-content somename.txt"
powershell "$f=(Get-Content somename.txt);$f[9]='heretext2';$f | set-content somename.txt"
but how can i add eighth and ninth lines to all *.txt files located in path C:\myfolder*.txt
Can someone explain me how to do it please...
Sorry for my English and Sorry if i didn't explaned my problem. i will try now:
I uses "*.uci" files, instead of *.txt files. i wrote txt because uci extensions are unknown for most of the people. These *.uci files are settings for chess engines with uci protocol.
So when you use chessbase program you have a lot of chess engines and each engine creates their "enginename.uci" file.
If you want to change the numbers of core used on your PC from 1 to 16 you need to do it manually by adding following information in *.uci file like this:
[OPTIONS]
Threads=1
That's why is better to make small batch or ps1 to change settings to all engines by adding these two lines with one click
Perhaps something like this PowerShell script would suit your task:
Get-ChildItem -Path 'C:\myfolder' -Filter '*.txt' | ForEach-Object {
$LineIndex = 0
$FileContent = Switch -File $_.FullName {Default {
$LineIndex++
If ($LineIndex -Eq 8) {#'
heretext1
heretext2
'#}
$_}}
Set-Content -Path $_.FullName -Value $FileContent}
Note:
Your code isn't adding lines, it is modifying existing lines. The solution below does the same.
Indices [8] and [9] access the 9th and 10th lines, not the 8th and 9th, given that array indexing is 0-based.
You need to call Get-ChildItem with your file-name pattern, C:\myfolder\*.txt, and process each matching file via ForEach-Object:
#echo off
powershell "Get-ChildItem C:\myfolder\*.txt | ForEach-Object { $f=$_ | Get-Content -ReadCount 0; $f[8]='heretext1'; $f[9]='heretext2'; Set-Content $_.FullName $f }"
Due to calling from a batch file (cmd.exe), the PowerShell command is specified on a single line; here's the readable version:
Get-ChildItem C:\myfolder\*.txt | # get all matching files
ForEach-Object { # process each
$f = $_ | Get-Content -ReadCount 0 # read all lines
$f[8] = 'heretext1'; $f[9] = 'heretext2' # update the 9th and 10th line
Set-Content $_.FullName $f # save result back to input file
}
Note:
Consider adding -noprofile after powershell, so as to suppress potentially unnecessary loading of profile files - see the documentation of the Windows PowerShell CLI, powershell.exe.
Using -ReadCount 0 with Get-Content greatly speeds up processing, because all lines are then read into a single array, instead of streaming the lines one by one, which requires collecting them in an array, which is much slower.
Note: If a given file has fewer than 10 lines, the above solution won't work, because you can only assign to existing elements of an array (an array is a fixed-size data structure). If you need to deal wit this case, insert the following after the $f = $_ | Get-Content -ReadCount 0 line, which inserts empty lines as needed to ensure that at least 10 lines are present:
if ($f.Count -lt 10) { $f += #('') * (10 - $f.Count) }
Easiest solution I can think of is using the -Index parameter provided in Select-Object for that.
Get-ChildItem -Path .\Desktop\*.txt | % { Get-Content $_.FullName | Select-Object -Index 7,8 } |
Out-File -FilePath .\Desktop\index.txt
Edit: based on your post.

Powershell script to run multiple input text files

I'm trying to remove spaces from input text file through PowerShell script.
I have hundreds if thousands of text files where I have to remove the spaces. I wrote the code to remove spaces. It is working fine for one text file but I want to execute more than one file .
gc C:\test\3.txt | where {$_ -ne ""} > C:\test\out.txt
In the above example I have more input files like 4.txt , 5.txt , 6.txt etc. I want to execute all the files in one shot and remove the spaces and write the 4out.txt, 5out.txt, 6out.txt, etc. to another folder.
It should just be a case of looping through your files and running the same line you already have.
Add some try-catch if you'd like to be safe.
$InputFiles = Get-ChildItem 'C:\test\'
$OutputPath = 'C:\test\out\'
ForEach($File in $InputFiles) {
Get-Content $File |
Where-Object {$_ -ne ''} |
Out-File (Join-Path -Path $OutputPath -ChildPath $File.Name)
}

PowerShell copy and rename multiple .csv files from 10+ subfolders

I'm searching for a way to copy multiple .csv files all named exactly the same, located in different folders (all of them are in the same dierctory) and merge them into 1 .csv file (I would like to skip copying the first line which is head, except from the first file and there is no rule how many lines are written in each .csv file, so the script should recognize written lines to know how many and which one to merge /to avoid blank lines).
This is what I tried so far:
$src = "C:\Users\E\Desktop\Merge\Input\Files*.csv"
$dst = "C:\Users\E\Desktop\Merge\Output"
Get-ChildItem -Path $src -Recurse -File | Copy-Item -Destination $dst
and this one:
Get-ChildItem -Path $src -Recurse -File | Copy-Item -Destination $dst |
ForEach-Object {
$NewName = $_.Name
$Destination = Join-Path -Path $_.Directory.FullName -ChildPath $NewName
Move-Item -Path $_.FullName -Destination $Destination -Force
}
any help please? :)
Since you are looking to merge the files you may as well read them all into PowerShell, and then output the whole thing at once. You could do something like:
$Data = Get-ChildItem -Path $src -Recurse -File | Import-Csv
$Data | Export-Csv $dst\Output.csv -NoTypeInformation
That may not be feasible if your CSV files are extremely large, but it is a simple way to merge CSV files if the header row is the same in all files.
Another method would be to just treat it as text, which is much less memory intensive. For that you would want to get a list of files, copy the first one intact, and then copy the rest of them skipping the header row.
$Files = Get-ChildItem $src -Recurse
$TargetFile = Join-Path $dst $Files[0].Name
$Files[0] | Copy-Item -Dest $TargetFile
#Skip the first file, and loop through the rest
$Files | Select -Skip 1 | ForEach-Object{
#Get the contents of the file, and skip the header row, then append the rest to the target
Get-Content $_ | Select -Skip 1 | Add-Content $TargetFile
}
Edit: Ok, I wanted to replicate the process so that I could figure out what was giving you errors. To do that I created 3 folders, and copied a .csv file with 4 entries into each folder, with all of the files named 'Files 06202018.csv'. I ran my code above, and it did what it should, but there was some file corruption where the second file would be appended directly to the end of the first file without a new line being created for it, so I changed things from just copying the first file, to reading it and creating a new file in the destination. The below code worked flawlessly for me:
$src = "C:\Temp\Test\Files*.csv"
$dst = "C:\Temp\Test\Output"
$Files = Get-ChildItem $src -Recurse
$TargetFile = Join-Path $dst $Files[0].Name
GC $Files[0] | Set-Content $TargetFile
#Skip the first file, and loop through the rest
$Files | Select -Skip 1 | ForEach-Object{
#Get the contents of the file, and skip the header row, then append the rest to the target
Get-Content $_ | Select -Skip 1 | Add-Content $TargetFile
}
That took the files:
C:\Temp\Test\Lapis\Files 06202018.csv
C:\Temp\Test\Malachite\Files 06202018.csv
C:\Temp\Test\Opal\Files 06202018.csv
And it combined those three files into a correctly merged file at:
C:\Temp\Test\Output\Files 06202018.csv
The only time that I had any issues is if I forgot to delete the target file before running this. Depending on how large these files are, and how much memory you have available, you could probably speed this up by changing the last two lines to this:
Get-Content $_ | Select -Skip 1
} | Add-Content $TargetFile
That would read all of the files in (other than the first one) and only write to the destination once, instead of having to get file lock, open the file for writing, write, and close the destination for each file.

PowerShell - Copying files in random folders to other folders

I have the following text file (tab delimited) that maps a specific file to a folder. I start with importing this csv:
SourcePathFile DestinationPath
C:\Test\Source\SourceDir 1\pic1.jpg C:\Test\Destination\Folder, 1
C:\Test\Source\SourceDir 1\Pic 2.jpg C:\Test\Destination\Folder 2
By using:
Import-csv -Delimiter `t "C:\Test\FileMapping.csv"
This gives me the array I want, so I figured that it would be a simple For-each to go through each line using
Copy-Item SourcePathFile DestinationPath
I'm clearly missing the general concepts
Assuming that your input file is valid TSV (is comma at the end of the line 2 is intended?) you can do this using pipeline:
Import-Csv -Path 'C:\Test\FileMapping.csv' -Delimiter "`t" |
ForEach-Object {Copy-Item -Path $_.SourcePathFile -Destination $_.DestinationPath}

How do I remove carriage returns from text file using Powershell?

I'm outputting the contents of a directory to a txt file using the following command:
$SearchPath="c:\searchpath"
$Outpath="c:\outpath"
Get-ChildItem "$SearchPath" -Recurse | where {!$_.psiscontainer} | Format-Wide -Column 1'
| Out-File "$OutPath\Contents.txt" -Encoding ASCII -Width 200
What I end up with when I do this is a txt file with the information I need, but it adds numerous carriage returns I don't need, making the output harder to read.
This is what it looks like:
c:\searchpath\directory
name of file.txt
name of another file.txt
c:\searchpath\another directory
name of some file.txt
That makes a txt file that requires a lot of scrolling, but the actual information isn't that much, usually a lot less than a hundred lines.
I would like for it to look like:
c:\searchpath\directory
nameoffile.txt
c:\searchpath\another directory
another file.txt
This is what I've tried so far, not working
$configFiles=get-childitem "c:\outpath\*.txt" -rec
foreach ($file in $configFiles)
{
(Get-Content $file.PSPath) |
Foreach-Object {$_ -replace "'n", ""} |
Set-Content $file.PSPath
}
I've also tried 'r but both options leave the file unchanged.
Another attempt:
Select-String -Pattern "\w" -Path 'c:\outpath\contents.txt' | foreach {$_.line}'
| Set-Content -Path c:\outpath\contents2.txt
When I run that string without the Set-content at the end, it appears exactly as I need it in the ISE, but as soon as I add the Set-Content at the end, it once agains carriage returns where I don't need them.
Here's something interesting, if I create a text file with a few carriage returns and a few tabs, then if I use the same -replace script I've been using, but uset to replace the tabs, it works perfect. Butr and n do not work. It's almost as though it doesn't recognize them as escape characters. But if I addr and `n in the txt file then run the script, it still doesn't replace anything. Doesn't seem to know what to do with it.
Set-Content adds newlines by default. Replacing Set-Content by Out-File in your last attempt in your question will give you the file you want:
Select-String -Pattern "\w" -Path 'c:\outpath\contents.txt' | foreach {$_.line} |
Out-File -FilePath c:\outpath\contents2.txt
It's not 'r (apostrophe), it's a back tick: `r. That's the key above the tab key on the US keyboard layout. :)
You can simply avoid all those empty lines by using Select-Object -ExpandProperty Name:
Get-ChildItem "$SearchPath" -Recurse |
Where { !$_.PSIsContainer } |
Select-Object -ExpandProperty Name |
Out-File "$OutPath\Contents.txt" -Encoding ASCII -Width 200
... if you don't need the folder names.