How can I replace a string in multiple files with a value from a list, sequentially in Powershell? - string

Lets say I have a bunch of text files with people's names, that all have this as the content:
number
I want to replace "number" with a value from a CSV or text file, sequentially, and based on the file name. CSV has two columns, name and number:
Joe 5551011000
Gary 5551011001
Clark 5551011002
So I want to find the text file named Joe, and replace the "number" with "5551011000", and the text file named Gary, and replace "number" with "5551011001".
Thank you!
I didn't get too far:
Get-ChildItem "C:\test\*.txt" -Recurse | ForEach-Object -Process {
(Get-Content $_) -Replace 'changeme', 'MyValue' | Set-Content $_
}
This gets me party there, but I don't know how to find a specific file, then replace "number" in that file with the correct value that matches the name.
I also tried a different approach, with manual entry, and it works, but I need it to just be automated:
get-childitem c:\Marriott -recurse -include *.txt |
select -expand fullname |
foreach {
$new = Read-Host 'What is the new value you want for ' $_
(Get-Content $_) -replace 'number',$new |
Set-Content $_
}

I would convert your CSV to a hashtable, then this gets pretty simple.
$ReplaceHT = #{}
Import-Csv c:\path\to\file.csv -Delimiter ' ' -Header 'FileName','Number' | ForEach-Object {$ReplaceHT.add($_.FileName,$_.Number)}
Get-ChildItem c:\Marriott -recurse -include *.txt -PipelineVariable 'File'|Where{$_.name -in $ReplaceHT.Keys} |ForEach-Object{
(Get-Content $File.FullName) -replace 'changeme', $ReplaceHT[$File.Name] | Set-Content $File.FullName
}

Related

Date content comparison if it is greater then todays date

I am looking for some script in PowerShell that will compare the date present in an inside text file as content and compare if that date is >today`+15 days then print the file name.
Also, if that script can compare the date as mentioned above along with the other string if both conditions are matching then print the file name.
The below command gives me the output for those which have matching string same as hello and was created 30 days back. But now I want to fulfill the above two conditions no matter when the file was created.
Get-ChildItem -Path C:\Users\vpaul\Downloads\functional-script\*.txt -Recurse | Select-String -Pattern 'Hello', 'Hell' | Where CreationTime -lt (Get-Date).AddDays(-6)| Export-Csv C:\Users\vpaul\Downloads\functional-script\File_Name.csv -NoTypeInformation
The output from Select-String doesn't have a CreationTime property, which is why your filtering fails - CreationTime doesn't resolve to anything so it's always "less than" any value you provide.
Either do the filtering on CreationTime before piping to Select-String:
Get-ChildItem ... |Where-Object CreationTime -lt (Get-Date).AddDays(-6) |Select-String 'Hell' | ...
Or use the Path property on the output from Select-String to look up the files attributes again:
Get-ChildItem ... |Select-String 'Hell' |Where-Object {(Get-ItemPropertyValue -LiteralPath $_.Path -Name CreationTime) -lt (Get-Date).AddDays(-6)} |...
Since it looks like you're trying to get and compare a date from a matched text string inside the file, as well as CreationTime file attribute... +15 Days and -6 Days respectively...
Example Text file Content:
Hello 4/1/2021
You could try something similar to this:
$ALL_RECURSED_TXTs = Get-ChildItem -Path '[Folder to Recurse]\*.txt' -Recurse | Where-Object { $_.CreationTime -lt (Get-Date).AddDays(-6) };
foreach($File in $ALL_RECURSED_TXTs) {
Get-Content -Path $File.FullName | Select-String -Pattern 'Hello', 'Hell' |
ForEach-Object {
# Find a RegEx match for your Date String that is in the File
$_ -match 'Hello\s(\d+\/\d+\/\d{4}).*' | Out-Null;
if((Get-date($matches[1])) -gt ((Get-Date).AddDays(15))) {
"$($File.FullName)" | Out-File -FilePath '[Path to Output]\MyPrintedFileNames.txt' -Append;
}
}
}
If you want to see your matched lines in your outfile...
"$_ : $($File.FullName)" | Out-File -FilePath '[Path to Output]\MyPrintedFileNames.txt' -Append;
"but now I want to fulfill the above two conditions no matter when the file was created."
Scrap the Where-Object filter on Get-ChildItem if you want all txt files.
Edit: Getting confused again. Lol. If your txt file date string is not on same line as your "Hello|Hell" it'll get more complex. Good Luck!

Powershell: Extract several strings from txt and create table out of it

I need to create a csv file out of values that are spread over many txt files. Here is an example for one of the txt files (they are all formatted the same way and stored in one folder, lets say c:\user\txtfiles):
System: asdf
Store: def
processid: 00001
Language: english
prodid: yellowshoes12
email: asdf#asdf.com
prodid: blueshoes34
some
other
text blabla
The result csv should look like this (i added values from another sample txt just to make it clear):
processid, prodid
00001, yellowshoes12
00001, blueshoes34
00002, redtshirt12
00002, greensocks34
That means that every product ID in the txt should be assigned to the one processid in the txt and added as single line to the csv.
I tried to reach the result as follows:
$pathtofiles = Get-ChildItem c:\user\txtfiles | select -ExpandProperty FullName
$parsetxt = $pathtofiles |
ForEach {
$orderdata = Import-Csv $_ |
Where-Object {($_ -like '*processid*') -or ($_ -like '*prodid*')} |
foreach {
write-output $orderdata -replace 'processid: ','' -replace 'prodid: ',''
}
}
$orderdata
So my intention was to isolate the relevant lines, delete everything that is not wanted, assign the values to variables and build a table out of it. One problem is that if I replace $orderdata from the end of the code into the end of the first foreach-loop nothing is printed. But after deliberating quite a while I am not sure if my approach is a good one anyway. So any help would be very appreciated!
Daniel
I think this is best done using a switch -Regex -File construct while iterating over the files in your folder.
# get the files in the folder and loop over them
$result = Get-ChildItem -Path 'c:\user\txtfiles' -Filter '*.txt' -File | ForEach-Object {
# the switch processes each line of a file and matches the regex to it
switch -Regex -File $_.FullName {
'^processid:\s+(\d+)' { $id = $matches[1] }
'^prodid:\s+(\w+)' { [PsCustomObject]#{'processid' = $id; 'prodid' = $matches[1]}}
}
} | Sort-Object processid, prodid
# output on console screen
$result
# output to CSV file
$result | Export-Csv -Path 'c:\user\txtfiles\allids.csv'
Result on screen:
processid prodid
--------- ------
00001 blueshoes34
00001 yellowshoes12
00002 greenshoes56
00002 purpleshoes88

Powershell Export-CSV formatting

I'm exporting data from multiple paths from our network drives. If a document is older than a certain date, I export it to a CSV file. But when I open up Excel, theres no formatting. Everything is all jammed up in the "A" column. I would like the "Name" to be in column A, "LastWriteTime" to be in column B, etc.
Here is my code:
foreach($path in $SharedFolder)
{
Get-ChildItem -Path $path -Recurse | Where-Object {!$_.PSIsContainer -and $_.LastWriteTime -lt $DateLimit} |
Select-Object Name, LastWriteTime, LastAccessTime, Length, DirectoryName |
Export-Csv -Path $HOME\Desktop\ExcelDoc.csv -NoTypeInformation
}
Any help would be appreciated.
Setting the delimiter to the system default delimiter should take care of this.
You can do this by adding -Delimiter to your command with the character specified.
For example: -Delimiter ';'

add colum to merged csv file

Ok heres what I have code wise:
$a = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\*.csv"
$b = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\merge.csv"
(get-content $a) | set-content $b
This pulls all the data of all the files into one merged file, but I need one additional item, I need to pull the name of the individual files and append it to the first column of the file for multiple files, several hundred at a time.
Not tested but something like this should do it:
$a = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\*.csv"
$b = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\merge.csv"
Get-ChildItem $a | % {
Import-Csv $_.Fullname | Add-Member -MemberType NoteProperty -Name 'File Name' -Value $_.Name
} | Export-Csv $b
Assuming the CSV files each have the same column headings, I would lean toward using Import-CSV instead of Get-Content so that you can work with the CSV contents as arrays of objects with named properties.
Then all you need to do is iterate through each item of the array and append a property containing the file path, which you can do using the Add-Member cmdlet. Once that's done, export the array of objects using the Export-CSV cmdlet.
$directory = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\"
$search = $directory + "*.csv"
$exportpath = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\merge.csv"
$paths = get-childitem $search
$objectArrays = #()
$paths | %{
$filepath = $_.fullname;
$objectArray = Import-CSV $filepath;
$objectArray | %{
Add-Member -inputobject $_ -Name "SourceFile" -Value $filepath -MemberType NoteProperty};
$objectArrays += $objectArray}
$objectArrays | export-csv -path $exportpath -notype
This puts the SourceFile property as the last column in the outputted CSV file
Ok, simplification... Search target folder, pipe to a ForEach-Object loop (shorthand % used), capture the file name as variable, import the CSV, add the sourcefile using the Select-Object cmdlet, convert it back to a CSV, end loop, pipe to destination file.
$a = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\*.csv"
$b = "C:\Users\some.deranged.character\Desktop\SomeAwfulPlace\Checklists\C_F\merge.csv"
GCI $a | %{$FileName=$_.Name;Import-CSV $_|Select #{l='SourceFile';e={$FileName}},*|ConvertTo-CSV -NoType} | set-content $b

Renaming many folders in PowerShell

I have over 1000+ files that have to be renamed.
The first set folder and/or files are grouped by location, so the first four characters are the same for each file; there are four-five different locations. I need to delete the first few characters of the folder's name.
Example:
Old File: ABC_Doe, Jane
New File: Doe, Jane
any suggestions as to the quickest way to carry this out?
I've tried all of the following:
1st Attempt
$a = Get-ChildItem C:\example
$b = Where-Object {$_.name -like “*ABC_*”}
$cmdlet_name = “Rename-Item”
$d = (cmdlet_name $a $b)
invoke-expression $d
2nd Attempt
$e = Get-ChildItem C:\example
$f = $e.TrimStart (“ABC_”)
3rd Attempt
Rename-Item -{$_.name -like “*ASD*”, “”}
Try this, get all child items (files only), remove abc_ by replacing them (with nothing) and rename each file. To rename files in sub-directories add the -Recurse switch to the Get-ChildItem command:
Get-ChildItem c:\example -Filter ABC_* | Where-Object {!$_.PSIsContainer} | Rename-Item -NewName { ($_.BaseName -replace '^ABC_') + $_.Extension }
UPDATE
Actually, this should work as well and is much shorter (no need to append the file extension cause renaming is performed on the file name).
Get-ChildItem c:\example -Filter ABC_* | Where-Object {!$_.PSIsContainer} | Rename-Item -NewName { $_.Name -replace '^ABC_' }
get-childItem ABC_* | rename-item -newname { $_.name -replace 'ABC_','' }
Source: get-help rename-item -full

Resources