I habe some files, they are all named after the same scheme (Wien - Number - Text, e.g. Wien - 001 - Text, Wien - 002 - Text). The files are nfo.
They have nfo files, but they are not correct, I was able to delete the wrong entry with advanced renamer.
Now I want to put the Number in line 8 of the nfo file.
I got this to work with only one file but I have several files in one folder and I haven't got this to work.
Here is my script
Get-ChildItem -Filter "*.nfo" | Foreach-Object{
$baseName = $_.BaseName
$array = $baseName -Split ' - '
$addToFile = '<episode>' + $array[1] + '</episode>'
$filePath = ".\*.nfo"
$fileContent = Get-Content $filePath
$lineNumber = "8"
$textToAdd = $addToFile
$fileContent[$lineNumber-1] = $textToAdd
$fileContent | Set-Content $filePath
}
As Bacon Bits already commented, you're not using the ful path and filename of the file where you already have that in $_.FullName.
Also, you could do with a lot less 'in-between' variables.
Try
$lineNumber = 8
Get-ChildItem -Path 'X:\WhereTheFilesAre' -Filter "* - *.nfo" | Foreach-Object {
$fileContent = Get-Content -Path $_.FullName
$fileContent[$lineNumber-1] = '<episode>{0}</episode>' -f ($_.BaseName -split ' - ')[1]
$fileContent | Set-Content $_.FullName
}
'<episode>{0}</episode>' -f ($_.BaseName -split ' - ')[1] is a very nice way to create a new string using the Format operator
Related
I am building am updating a script which imports a large CSV file and then splits it into lots of separate CSV files based on the value in the first two columns
so POIMP_NL_20210306.csv which contains:
DOC_NUMBER|COMMENTS|ITEM|QTY|SUPPLIER
P-100-1234|JANE|5059585896978|2|"JOES SUPPLIES"
P-100-1234|JANE|5059585896985|2|"JOES SUPPLIES"
P-100-6666|TED|5059585896992|1|"ACTION TOYS"
must be split into POIMP_P-100-1234_JANE.csv containing
P-100-1234|JANE|5059585896978|2|"JOES SUPPLIES"
P-100-1234|JANE|5059585896985|2|"JOES SUPPLIES"
and POIMP_P-100-6666_TED.csv
P-100-6666|TED|5059585896992|1|"ACTION TOYS"
The problem I am trying to solve is preserving the quotes in just the SUPPLIER column
Since ConvertTo-Csv adds quotes to everything, I use a % { $_ -replace '"', ""} to remove these all before the out-file is created but of course it removes these from the SUPPLIER column 2
Here is my script which perfectly splits the big file into smaller files by DOC_NUMBER and COMMENTS but removes all quotes:
$basePath = "C:\"
$archivePath = "$basePath\archive\"
$todaysDate = $(get-date -Format yyyyMMdd)
$todaysFiles = #(
(Get-ChildItem -Path $basePath | Where-Object { $_.Name -match 'POIMP_' + $todaysDate })
)
cd $basePath
foreach ($file in $todaysFiles ) {
$fileName = $file.ToString()
Import-Csv $fileName -delimiter "|" | Group-Object -Property "DOC_NUMBER","COMMENTS" |
Foreach-Object {
$newName = $_.Name -replace ",","_" -replace " ",""; $path=$fileName.SubString(0,8) + $newName+".csv" ; $_.group |
ConvertTo-Csv -NoTypeInformation -delimiter "|" | % { $_ -replace '"', ""} | out-file $path -fo -en ascii
}
Rename-Item $fileName -NewName ([io.path]::GetFileNameWithoutExtension("$fileName") + "_Original.csv")
Move-Item (Get-ChildItem -Path $basePath | Where-Object { $_.Name -match '_Original' }) $archivePath -force
}
And here is another script which I found online and amended and which successfully leaves quotes in just the SUPPLIER column by first adding double back ticks and then replacing these with quotes after all others have been removed
$ImportedCSV = Import-CSV "C:\POIMP_NL_20210306.csv" -delimiter "|"
$NewCSV = Foreach ($Entry in $ImportedCsv) {
$Entry.SUPPLIER = '¬¬' + $Entry.SUPPLIER + '¬¬'
$Entry
}
$NewCSV |
ConvertTo-Csv -NoTypeInformation -delimiter "|" | % { $_ -replace '"', ""} | % { $_ -replace '¬¬', '"'} | out-file "C:\updatedPO.csv" -fo -en ascii
I just can't merge these scripts to achieve the desired result as I can't seem to reference the correct object. I'd really appreciate your help! Thanks
Any good CSV reader should be able to handle quotes around csv fields, even when not really needed.
Having said that, It is your explicit wish to only have quotes around the field in the SUPPLIER column. (Note, in your example there is a trailing space after that column name)
In this case, I think this would help.
Not only does it surround the SUPPLIER fields with quotes, but also saves the data as separate files using the values from column DOC_NUMBER and COMMENTS per group found in the csv
$path = 'D:\Test'
$fileIn = Join-Path -Path $path -ChildPath 'POIMP_NL_20210306.csv'
# import the csv file and group first two columns
Import-Csv -Path $fileIn -Delimiter '|' | Group-Object -Property "DOC_NUMBER","COMMENTS" | ForEach-Object {
$headerDone = $false
$data = foreach ($item in $_.Group) {
if (!$headerDone) {
$item.PsObject.Properties.Name -join '|'
$headerDone = $true
}
$item.SUPPLIER = '"{0}"' -f $item.SUPPLIER
$item.PsObject.Properties.Value -join '|'
}
# create a new filename like 'POIMP_P-100-1234_JANE.csv'
$fileOut = Join-Path -Path $path -ChildPath ('POIMP_{0}_{1}.csv' -f $_.Group[0].DOC_NUMBER, $_.Group[0].COMMENTS)
# save the data not using Export-Csv because that will add quotes around everything (in PowerShell 5)
$data | Set-Content -Path $fileOut -Force
}
Output
POIMP_P-100-1234_JANE.csv
DOC_NUMBER|COMMENTS|ITEM|QTY|SUPPLIER
P-100-1234|JANE|5059585896978|2|"JOES SUPPLIES"
P-100-1234|JANE|5059585896985|2|"JOES SUPPLIES"
POIMP_P-100-6666_TED.csv
DOC_NUMBER|COMMENTS|ITEM|QTY|SUPPLIER
P-100-6666|TED|5059585896992|1|"ACTION TOYS"
If you are Powershell 7 or later, you can use
$yourdata | ConvertTo-Csv -NoTypeInformation -QuoteFields "SUPPLIER" -Delimiter "|" |
Out-File ...
or you could use
$yourdata | Export-Csv -NoTypeInformation -QuoteFields "SUPPLIER" `
-Delimiter "|" -Path <path-to-output-file>.csv
You can also use -UseQuotes AsNeeded to let the converter add quoting where it thinks it makes sense, otherwise just specify the fields you want quoted.
I have written the below conditional script to go through the files in the directory and replace the one text in all files only if file contains the word as 'Health'
cd -Path "\\shlhfilprd08\Direct Credits\Temp2"
ForEach ($file in (Get-ChildItem -Path "\\shlhfilprd08\Direct Credits\Temp2"))
{
$filecontent = Get-Content -path $file -First 1
if($filecontent -like '*Health*'){$filecontent = $filecontent -replace 'TEACHERF','UniHlth '}
Set-Content $file.PSpath -Value $filecontent
}
I come across with two issues such as
If the ($filecontent -like 'Health'), it is replacing the word in first raw and deleting other rows along with replace.I do not want that to happen
I'm getting set-content to path is denied error message for file content does not contain the Health text
Can you try with this
cd -Path "\\shlhfilprd08\Direct Credits\Temp2"
$configFiles = Get-ChildItem . *.config -rec
foreach ($file in $configFiles)
{
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace "TEACHERF", "UniHlth " } |
Set-Content $file.PSPath
}
I would try this; it worked for me in a little file
(make a small copy of a few data into a new folder and test it there)
$path = "\\shlhfilprd08\Direct Credits\Temp2"
$replace ="TEACHERF" #word to be replaced
$by = "UniHlth " #by this word (change $replace by $by)
gci $path -file | %{
foreach($line in $(Get-content $_.Fullname)){
if($line -like $replace){
$newline = $line.Replace($($replace),$($by))
Set-Content $_.FullName $newline
}
}
}
I'm trying to write a Powershell script which will take a several very long space-separated files and export some columns to similarly-named CSV files.
I do have a successful version:
Foreach ($file in $files) {
$WriteString=""
$outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"
Get-Content -Path $path"\"$file | Select-Object -Skip $lines | ForEach-Object{
$ValueArray = ($_ -split "\s+")
$WriteString += $ValueArray[1] + "," + $ValueArray[2] + "," + $ValueArray[3] + "`n"
}
Add-Content -Path $outfile -Value $Writestring
}
This works, but is extremely slow - it takes over 16 hours for the script to fully run. The main cause (I think) is adding to the string. I've tried improving this using a hashtable:
Foreach ($file in $files) {
$outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"
$ParseLines = Get-Content -Path $path"\"$file | Select-Object -Skip $lines
$OutputData = ForEach ($Line in $ParseLines) {
$ValueArray = ($Line -split "\s+")
$Line | Select-Object $ValueArray[1], $ValueArray[2], $ValueArray[3]
}
$OutputData | Export-CSV -Path $outfile #-NoTypeInformation
}
However, this is only exporting one line of the hashtable:
#TYPE Selected.System.String
"636050.000","7429825.000","77.438"
,,
,,
,,
,,
,,
,,
If I change the last line to:
Set-Content -Path $outfile -Value $OutputData
then the output becomes:
#{636050.000=; 7429825.000=; 77.438=}
#{636075.000=; 7429825.000=; 75.476=}
#{636100.000=; 7429825.000=; 74.374=}
#{636125.000=; 7429825.000=; 73.087=}
#{636150.000=; 7429825.000=; 71.783=}
#{636175.000=; 7429825.000=; 70.472=}
I'm clearly doing something wrong with either the hashtable or Export-CSV, but I can't figure it out. Any help will be greatly appreciated.
As requested below, here's part of one source file. I cut out all non-data rows, and don't include headers in my output CSV, as the input program (that the CSV files go into) doesn't require them, and the outputs are self-evident (Not much chance of getting the X, Y and Z values wrong just by looking at the data).
*
* DEFINITION
* HEADER_VARIABLES 3
* QUALITIES C 16 0 key
* DATE C 12 0
* TIME C 12 0
* VARIABLES 4
* X F 12 3
* Y F 12 3
* Z F 12 3
* gcmaq0.drg F 12 3
*
* 1 2 3 4
*23456789012345678901234567890123456789012345678
* X| Y| Z| gcmaq0.drg|
*
* HEADER:QUALITIES 29Aug2018 13:53:16
636575.000 7429800.000 75.551 75.551
636600.000 7429800.000 77.358 77.358
636625.000 7429800.000 78.823 78.823
636650.000 7429800.000 80.333 80.333
636675.000 7429800.000 82.264 82.264
636700.000 7429800.000 84.573 84.573
636725.000 7429800.000 87.447 87.447
Avoid slow operations like appending to strings (or arrays) in a loop. Change this:
Get-Content -Path $path"\"$file |
Select-Object -Skip $lines |
ForEach-Object {
$ValueArray = ($_ -split "\s+")
$WriteString += $ValueArray[1] + "," + $ValueArray[2] + "," + $ValueArray[3] + "`n"
}
Add-Content -Path $outfile -Value $Writestring
into this:
Get-Content -Path "${path}\${file}" |
Select-Object -Skip $lines |
ForEach-Object {
($_ -split "\s+")[1..3] -join ','
} |
Set-Content -Path $outfile
Replace Set-Content with Add-Content if you actually want to append to an existing file.
Export-Csv works with objects. It expects properties and values - what you're providing (judging from the Set-Content results) is hashtable with keys only.
One way around this is to create an object and increment values from each line.
Foreach ($file in $files) {
$outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"
$ParseLines = Get-Content -Path $path"\"$file | Select-Object -Skip $lines
ForEach ($Line in $ParseLines) {
$ValueArray = ($Line -split "\s+")
[array]$OutputData += [pscustomobject]#{
header1 = $ValueArray[1]
header2 = $ValueArray[2]
header3 = $ValueArray[3]
}
}
$OutputData | Export-CSV -Path $outfile #-NoTypeInformation
}
Not sure if this is the optimal way if you have very large files - am sure a regex guru can come up with something more efficient.
The solution above by Ansgar Wiechers worked best, but I also found a second way of doing it at this SO question. It uses a ArrayList to store the hashtable, then writes the ArrayList. This method is almost, but not quite as fast as Ansgar's solution. (About 10x faster than string method, vs 12x for regex method)
Foreach ($file in $files) {
[System.Collections.ArrayList]$collection = New-Object System.Collections.ArrayList($null)
$outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"
$ParseLines = Get-Content -Path $path"\"$file | Select-Object -Skip $lines
$OutputData =#{}
ForEach ($Line in $ParseLines) {
$ValueArray = ($Line -split "\s+")
$OutputData.Easting = $ValueArray[1]
$OutputData.Northing = $ValueArray[2]
$OutputData.ZValue = $ValueArray[3]
$collection.Add((New-Object PSObject -Property $OutputData)) | Out-Null
}
$collection | Export-CSV -Path $outfile -NoTypeInformation
}
I would like to ask you, how to check the all files in folder for /x00. Replace /x00 with nothing and save the checked files in different folder with the same fail name.
Here is the code only for one file:
(Get-Content "C:\source\filename.dat") -replace "\x00", "" |
Set-Content "C:\temp\filename.dat"
Based on your comment of removing space between the outputs (although this method will remove ALL whitespace in the file):
#Requires -Version 4
Get-ChildItem -Path 'C:\Users\georgi\Desktop\dat' -PipelineVariable 'f' |
ForEach-Object {
(Get-Content -Path $f.FullName) -replace '\s' |
Set-Content -Path ('C:\Users\georgi\Desktop\5\' + $f.Name)
}
If you're specifically looking for the \x00 hex character (NUL), here's a v2 compatible snippet:
$Files = Get-ChildItem -Path $Path
ForEach ($File in $Files)
{
(Get-Content -Path $File.FullName) -replace '\s' -replace "`0" |
Set-Content -Path ('C:\Temp\' + $File.Name)
}
This is the script in Python :
import os
source_path = "C:\\Users\\georgi\\Desktop\\dat\\"
target_path = "C:\\Users\\georgi\\Desktop\\5\\"
for source in os.listdir(source_path):
with open(source_path + source, 'r') as s, open(target_path + source, 'a') as t:
for line in s:
t.write(line.replace('\x00', '').replace(' ',''))
I hope it helps you to understand what I want.
I am trying to
Create a CD_TMP file in each WE*.MS directory
Set content by processing the AHD*.TPL and ADT*.TPL files
Rename the AHD*.TPL to AHD*.TPL.Done and ADT*.TPL to AHD*.TPL.Done.
When there is only one WE.20150408.MS directory, the scripts works fine
but when there are more than one directories (i.e. WE.20150408.MS, WE.20151416.MS,WE.20140902.MS), it does not work and gives error message:
Get-Content: An object at specified path AHD*TPL does not exist of has been filtered by the -Include or -Exclude parameter.
At C:\Temp\Script\Script.ps1:24 Char:14
+ $content = Get=Content -path $AHD
+ CatagoryInfo :ObjectNotFound: (System.String[]:Strint[1) [Get-Content], Exception
+ FullyQualifiedErrorID: ItemNotFound,Micorsoft.Powershell.Commands.GetContentCommand
SCRIPT:
$SOURCE_DIR = "C:\Work"
$Work_DIR = "WE*MS"
$WE_DIR = "$SOURCE_DIR\$Work_DIR"
$AHD = "AHD*TPL"
$ADT = "ADT*TPL"
$AHD_FILES = $SOURCE_DIR
$CD_TMP = "CD_TMP"
$Str1 = "TEMP"
##############
Set-Location $WE_DIR
New-Item -Path "CD_TMP" -type file -force
#############
foreach ( $File in ( get-childitem -name $WE_DIR))
{
$content = Get-Content -path $AHD
$content | foreach {
If ($_.substring(0,4) -NotLike $Str1)
{
'0011' + '|' + 'HD' + '|' + 'AHD' + $_
}
} | Set-Content $CD_TMP
}
Get-ChildItem AHD*.TPL| ForEach {Move-Item $_ ($_.Name -replace ".TPL$",
".TPL.Done")}
##############
foreach ( $File in ( get-childitem -name $WE_DIR))
{
$content = Get-Content -path $ADT
$content | foreach {
If ($_.substring(0,4) -NotLike $Str1)
{
'0022' + '|' + 'DT' + '|' + 'ADT' + $_
}
} | Set-Content $CD_TMP
}
Get-ChildItem ADT*TPL| ForEach {Move-Item $_ ($_.Name -replace ".TPL$",
".TPL.Done")}
PAUSE
Is it first giving the error Set-Location : Cannot set the location because path 'C:\Work\WE*MS' resolved to multiple containers. ? That's what I expect it to say when it fails.
Then, because it can't change into the folder, it can't find any AHD files.
Does it work properly for one folder? It writes the CD_TMP file for AHD files, then overwrites it for ADT files. That doesn't seem right.
Also you can make it a bit more direct by changing:
putting lots of things in $CAPITAL variables at the start, then using them once, or never.
The .substring() -notlike test to use .startswith()
The string building with ++++ into a single string
The renaming into a Rename-Item with -NewName scriptblock
I'm thinking this:
$folders = Get-ChildItem "C:\Work\WE*MS" -Directory
foreach ($folder in $folders) {
# AHD files
$content = Get-Content "$folder\AHD*.TPL"
$content = $content | where { -not $_.StartsWith('TEMP') }
$content | foreach {"0011|HD|AHD$_"} | Set-Content "$folder\CD_TMP" -Force
Get-ChildItem "$folder\AHD*.TPL" | Rename-Item -NewName {$_.Name + '.Done'}
# ADT files
$content = Get-Content "$folder\ADT*.TPL"
$content = $content | where { -not $_.StartsWith('TEMP') }
$content | foreach {"0011|HD|ADT$_"} | Add-Content "$folder\CD_TMP"
Get-ChildItem "$folder\ADT*.TPL" | Rename-Item -NewName {$_.Name + '.Done'}
}
Although I don't know what the input or output should be, so I can't test it. NB. it now does Add-Content to append to the CD_TMP file, instead of overwriting it.
There's still alot of redundancy with $content, but the lines mostly stand alone like this.