I have files which need to be modified according to mapping provided in CSV. I want to read each line of my txt file and depending if specified value exist I want to replace other strings in that line according to my CSV file (mapping). For that purpose I have used HashTable. Here is my ps script:
$file ="path\map.csv"
$mapping = Import-CSV $file -Encoding UTF8 -Delimiter ";"
$table = $mapping | Group-Object -AsHashTable -AsString -Property Name
$original_file = "path\input.txt"
$destination_file = "path\output.txt"
$content = Get-Content $original_file
foreach ($line in $content){
foreach ($e in $table.GetEnumerator()) {
if ($line -like "$($e.Name)") {
$line = $line -replace $e.Values.old_category, $e.Values.new_category
$line = $line -replace $e.Values.old_type, $e.Values.new_type
}
}
}
Set-Content -Path $destination_file -Value $content
My map.csv looks as follows:
Name;new_category;new_type;old_category;old_type
alfa;new_category1;new_type1;old_category1;old_type1
beta;new_category2;new_type2;old_category2;old_type2
gamma;new_category3;new_type3;old_category3;old_type3
And my input.txt content is:
bla bla "bla"
buuu buuu 123456 "test"
"gamma" "old_category3" "old_type3"
alfa
When I run this script it creates exactly the same output as initial file. Can someone tell me why it didn't change the line where "gamma" appears according to my mapping ?
Thanks in advance
Couple of things to change.
Firstly there is no need to change $mapping to a hash, Import-Csv already gives you an object array to work with.
Secondly, if you want to update the elements of $content, you need to use a for loop such that you can directly access modify them. Using a foreach creates a new variable in the pipeline and you were previously modifying it but then never writing it back to $content
Below should work:
$file ="map.csv"
$mapping = Import-CSV $file -Encoding UTF8 -Delimiter ";"
$original_file = "input.txt"
$destination_file = "output.txt"
$content = Get-Content $original_file
for($i=0; $i -lt $content.length; $i++) {
foreach($map in $mapping) {
if ($content[$i] -like "*$($map.Name)*") {
$content[$i] = $content[$i] -replace $map.old_category, $map.new_category
$content[$i] = $content[$i] -replace $map.old_type, $map.new_type
}
}
}
Set-Content -Path $destination_file -Value $content
Related
I have file1.html with this lines:
<bogus></bogus>
<title>Something goes here</title>
<TheEnd>END</TheEnd>
I made 3 different PowerShell scripts with regex in order to change this line: <title>Something goes here</title>:
$path = 'c:\Folder1\file1.html'
$Content = Get-Content -Path $path
foreach($Line in $Content){
$Line -replace "<title>(.*?)</title>",'$1 NEW is now there!' #This regex selects everything between tags and make a replace:
}
Set-Content -Path $Path -Value $Line
AND
$Content = Get-Content -Path c:\Folder1\file1.html
foreach($Line in $Content){
$Line -replace "<title>(.*?)</title>",'$1 NEW is now there!' #This regex selects everything between tags and make a replace:
}
Set-Content -Path $Path -Value $Line
OR
$path = 'c:\Folder1\file1.html'
$Content = Get-Content -Path $path
$GetTitle = [regex]"<title>(.*?)</title>"
foreach($Line in $Content){
$Line -replace $GetTitle,'$1 NEW is now there!' #This regex selects everything between tags and make a replace:
}
Set-Content -Path $Path -Value $Line
The output should be.
<bogus></bogus>
<title>NEW is now there!</title>
<TheEnd>END</TheEnd>
Mention that all my codes are working in PowerShell, but does not make any change in File1.html. That is the problem. can anyone correct my codes?
With regex -replace, you need to think what you want to keep and capture that in backreferences.
In your case, you want to retain <title> and </title>, and replace what is in between those tags.
Change the regex to '(<title>).*?(</title>)'.
Also, you can use the -Raw switch on Get-Content to read the file as a single multilined string, do the replacement and pipe the result straight through to Set-Content
$path = 'c:\Folder1\file1.html'
(Get-Content -Path $path -Raw) -replace '(<title>).*?(</title>)', '$1NEW is now there!$2' |
Set-Content -Path $Path
Details:
'$1' + Insert the text that was last matched by capturing group number 1
' NEW is now there!' + Insert the character string “ NEW is now there!” literally
'$2' Insert the text that was last matched by capturing group number 2
$path = 'c:\Folder1\file1.html'
$Content = Get-Content -Path $path
$newContent =#()
$RegexForTitle = '(?<=title>).*(?=</title>)'
foreach($Line in $Content)
{
$newContent += $Line -replace $RegexForTitle,'NEW IS NOW HERE!'
}
Set-Content -Path $Path -Value $newContent
#optional this line
'| Out-File -path file1.html'
There is a data in my text file. It is a list of IP addresses in column, I want to convert it into csv file and with that also i want to create 3 more columns and put the same data that is present in the first column
so far have created this
$output_file= "D:\output.csv"
#the headers i have given below
Set-Content D:\output.csv 'record,stream,library,thumbnail'
$input_file= "D:\input1.txt"
$lines = Get-Content $input_file
foreach ($line in $lines) {
Add-Content -Path $output_file -Value "$line, $line, $line, $line"
}
Why not go the object way?
$output_file = "D:\output.csv"
$input_file = "D:\input1.txt"
Get-Content -Path $input_file | ForEach-Object {
# output an object
[PsCustomObject]#{
record = $_
stream = $_
library = $_
thumbnail = $_
}
} | Export-Csv -Path $output_file -NoTypeInformation
I have this powershell code which should replace every occurrence of a string in every file in the directory with a new string.
This works, however an empty line is added in the end.
What causes this, and how can this be nicely avoided?
$files = Get-ChildItem $currentDir *.* -recurse
foreach ($file in $files)
{
$find = "placeholder"
$replace = "newvalue"
$content = Get-Content $($file.FullName) -Raw
$content -replace $find,$replace | Out-File $($file.FullName)
}
Simply removing the last line is not a good solution since sometimes my files will contain an empty line which I want to keep.
You could use the -NoNewline parameter to prevent Out-File from appending the extra line at the end of the file.
$content -replace $find,$replace | Out-File $($file.FullName) -NoNewline
Note: this was added in PowerShell 5.0
I am limited to PS version 4, and this is what I used
$files = Get-ChildItem $currentDir . -recurse
$find = "placeholder"
$replace = ""newvalue"
foreach ($file in $files)
{
$content = Get-Content $($file.FullName) -Raw | ForEach-Object { $_ -replace $find,$replace}
$content = $content -join "`r`n"
$content | Set-Content $($file.FullName)
}
Note that this only works if it is ok to store the complete file in memory.
I would like to use PowerShell to parse a log, and output to a CSV file.
What is the basic way to accomplish this?
Each log format involves it own logic when doing pattern matching, but I would propose to you something like:
(test.log is something like:
date - context - type - msg)
$file = get-content test.log
$pattern = '^(.*) - (.*) - (.*) - (.*)$'
$findings = $file | select-string -pattern $pattern
$lines = foreach($f in $findings) {
if ($f -match $pattern) {
$line = #{
'date' = $matches[1]
'context' = $matches[2]
'level' = $matches[3]
'message' = $matches[4]
}
new-object -typename psobject -property $line
}
}
$lines | export-csv -notypeinformation -delimiter ';' -path 'test.csv'
Get-Content | do something | Export-CSV
Working on a code which will replace a set of characters from a text files in a folder. IS there a way where it can do it for all the files in the folder. I am using a Windows 7 OS and Powershell Version 3.Attaching the code which I have. The issue is it creates a new file when I run the code (New_NOV_1995.txt) but it doesn't change any character in the new file as mentioned in the code. Help very much Appreciated.
$lookupTable = #{
'¿' = '|'
'Ù' = '|'
'À' = '|'
'Ú' = '|'
'³' = '|'
'Ä' = '-'
}
$original_file = 'C:\FilePath\NOV_1995.txt'
$destination_file = 'C:\FilePath\NOV_1995_NEW.txt'
Get-Content -Path $original_file | ForEach-Object {
$line = $_
$lookupTable.GetEnumerator() | ForEach-Object {
if ($line -match $_.Key)
{
$line = $line -replace $_.Key, $_.Value
}
}
$line
} | Set-Content -Path $destination_file
In the following example, I'm assuming that H:\Replace_String is a directory. In your code above, you don't have a backslash so it would only select files in the root of H:.
$configFiles = Get-ChildItem -path H:\Replace_String\*.txt
foreach ($file in $configFiles)
{
(Get-Content $file) |
Foreach-Object { $_ -replace "Cat", "New_Cat" } |
Foreach-Object { $_ -replace "Dog", "New_Dog" } |
Set-Content $file
}
The (original) answer proposed by Tony Hinkle needs another loop. The reason for this is that Get-Content produces an array. Each line represents an element of the array.
$configFiles = Get-ChildItem -path 'H:\Replace_String\*.txt'
foreach ($file in $configFiles){
$output = #()
$content = Get-Content $file
foreach ($line in $content) {
$line = $content.Replace("Cat", "New_Cat")
$line = $content.Replace("Dog", "New_Dog")
$output += $line
}
$output | Set-Content -Path $file
}
Edit: I noticed that Tony Hinkle's answer was modified as I posted this. He's sending everything through a pipeline where I'm storing the array in a variable then looping through. The pipeline method is probably more memory efficient. The variable with second loop for each element of the array is more easily modified to do more than just the two replacments.