Powershell update string in each file within all sub-folders - powershell

I have a set of config files stored in each subfolder within a directory. These config files only contain a single string in the format XXX_YYYYMMDD where XXX is a number e.g. 006, 007 etc, so an example string would be 006_20150101. I want the powershell script to replace the XXX number with a new one in each of these config files. I'm using the below script to achieve that and it works fine. However, the issue is that it puts a new line character (ENTER) at the end of the string which I don't want. Any way to fix this?
$sourceDir = "C:\Users\001"
$configFiles = Get-ChildItem $sourceDir *.dat -rec
foreach ($file in $configFiles)
{
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace "006", "007" } |
Set-Content $file.PSPath
}

By default set-content ends with a newline, use -NoNewline to not have this behavior:
Set-Content -path $file.PSPath -NoNewline
I dont know if u can use this but you can use regex replace to match the first 3 digits in the string:
$regex = "^\d{3}"
# matches any 3 digits("\d{3}") at the beginning("^") of a string
"124_20201030" -replace $regex, "007"

Related

Duplicate lines in a text file multiple times based on a string and alter duplicated lines

SHORT: I am trying to duplicate lines in all files in a folder based on a certain string and then replace original strings in duplicated lines only.
Contents of the original text file (there are double quotes in the file):
"K:\FILE1.ini"
"K:\FILE1.cfg"
"K:\FILE100.cfg"
I want to duplicate the entire line 4 times only if a string ".ini" is present in a line.
After duplicating the line, I want to change the string in those duplicated lines (original line stays the same) to: for example, ".inf", ".bat", ".cmd", ".mov".
So the expected result of the script is as follows:
"K:\FILE1.ini"
"K:\FILE1.inf"
"K:\FILE1.bat"
"K:\FILE1.cmd"
"K:\FILE1.mov"
"K:\FILE1.cfg"
"K:\FILE100.cfg"
Those files are small, so using streams is not neccessary.
I am at the beginning of my PowerShell journey, but thanks to this community, I already know how to replace string in files recursively:
$directory = "K:\PS"
Get-ChildItem $directory -file -recurse -include *.txt |
ForEach-Object {
(Get-Content $_.FullName) -replace ".ini",".inf" |
Set-Content $_.FullName
}
but I have no idea how to duplicate certain lines multiple times and handle multiple string replacements in those duplicated lines.
Yet ;)
Could point me in the right direction?
To achieve this with the operator -replace you can do:
#Define strings to replace pattern with
$2replace = #('.inf','.bat','.cmd','.mov','.ini')
#Get files, use filter instead of include = faster
get-childitem -path [path] -recurse -filter '*.txt' | %{
$cFile = $_
#add new strings to array newData
$newData = #(
#Read file
get-content $_.fullname | %{
#If line matches .ini
If ($_ -match '\.ini'){
$cstring = $_
#Add new strings
$2replace | %{
#Output new strings
$cstring -replace '\.ini',$_
}
}
#output current string
Else{
$_
}
}
)
#Write to disk
$newData | set-content $cFile.fullname
}
This gives you the following output:
$newdata
"K:\FILE1.inf"
"K:\FILE1.bat"
"K:\FILE1.cmd"
"K:\FILE1.mov"
"K:\FILE1.ini"
"K:\FILE1.cfg"
"K:\FILE100.cfg"

Replacing text only in lines that match a criteria, using the pipeline

My goal is to replace specific texts in specific lines in a text file, and I want to do that using the pipeline.
At first, I tried to write the code for the text replacement, without the condition that set the replacement to happen only in specific lines:
$fileName = Read-Host "Enter the full path of the file, without quotes"
(Get-Content -Path $fileName -Encoding UTF8) |
ForEach-Object { $_ -replace "01", "January " } |
Set-Content -Path $fileName -Encoding UTF8
It seems that it works. But then, I inserted an IF statement to the pipeline:
$fileName = Read-Host "Enter the full path of the file, without quotes"
(Get-Content -Path $fileName -Encoding UTF8) |
ForEach-Object { if ($_ -match "Month") {$_ -replace "03", "March"} } |
Set-Content -Path $fileName -Encoding UTF8
When I ran the last script, at the end of the process I got a file that includes only the lines that matched the if Statement. If I'm understanding correctly what happened, it seems that only the lines that match the if statement are passed to the next stage in the pipeline. So I understand why the output of the process, but I still can't figure how to solve this - How to pass all the lines in the files through all the stages of the pipeline, but to still make the text replacements to happen only in specific lines that match a specific criteria.
Could you please assist me with this issue?
Please notice that I would like not to use a temporary file for this and also remember that I prefer an elegant way of doing this, using the pipeline.
You have to add else statement like:
(Get-Content -Path $fileName -Encoding UTF8) |
Foreach-Object { If ($_ - match "Month") { $_ -replace "03", "March"} else { $_ } } |
Set-Content -Path $fileName - Encoding UTF8
Without else you didn't put line in pipeline. So your if was like filter
Depending on what your input data looks like you may not need a nested conditional (or a ForEach-Object) at all. If your input looks for instance like this:
Month: 03
you can do the replacement like this:
(Get-Content -Path $fileName -Encoding UTF8) -replace '^(.*Month.*)03','$1March' |
Set-Content -Path $fileName -Encoding UTF8
That will modify just the lines matching the pattern (^(.*Month.*)03) and leave everything else unchanged.

Renaming a file name to exclude the first couple parts Powershell

I have over a million files like such: First_Last_MI_DOB_ and lots more information. Is there a way I can run a rename script that can remove just the first, last, Mi, and DOB from the file name, but keep the stuff after that? Thank you.
Edited from my answer to this question: Parse and Switch Elements of Folder Names using Powershell
# Path to folder
$Path = '.\'
# Regex to match "ID_000000..."
$Regex = 'ID_\d+.*$'
# Get all objects in path
Get-ChildItem -Path $Path |
# Select only objects that are not directory and name matches regex
Where-Object {!$_.PSIsContainer -and $_.Name -match $Regex} |
# For each such object
ForEach-Object {
# Rename object
Rename-Item -Path $_.FullName -NewName $Matches[0]
}
UPDATE #1 : It seems that you need to write a regex that will match a required part of the name and then use it in to rename a document.
Assuming that file name is x-John_Doe_._DOB_01-11-1990_M_ID_000000_TitleofDocument_DateofDocument_Docpagenu‌​mber_, here is couple of the examples:
Regex (https://regex101.com/r/gI0fZ2/2): (ID_\d+.*)$ - will match ID_{ONE_OR_MORE_DIGITS}{ANY_CHARACTERS}
Result:ID_000000_TitleofDocument_DateofDocument_Docpagenu‌​mber_
Regex (https://regex101.com/r/gI0fZ2/1): \d{4}_(M|F)_(.*)$ - will match {4_DIGITS}_M_{or}_F_ and capture everything after that in capture group.
Result:
1st match - M
2nd match (the one to use) - ID_000000_TitleofDocument_DateofDocument_Docpagenu‌​mber_
UPDATE #2:
All the names in each file are different, a long with different ID's.
For example: John_Doe_DOB_01/01/01_ID_000000 and the next file name
could be: John_Smith_DOB_01/02/01_ID_100000 and so on. I am thinking I
would just want to read the file name in as a string, split it by _
and then make the new file name the stuff from [4] and after. Is there
a way to do that?
Sure, you can do that, but I'd recommend a regex approach, because it would work for every filename that has ID_0xxxx string, no matter of what. I've modified my initial example with first regex, so it should work for you.
But if you'd like to try splitting approach, here is how to do it:
# Path to folder
$Path = '.\'
# Filename separator
$Separator = '_'
# Get all objects in path
Get-ChildItem -Path $Path |
# Select only objects that are not directory and name matches regex
Where-Object {!$_.PSIsContainer} |
# For each such object
ForEach-Object {
# Generate new name
$NewName = ($_.Name -split $Separator | Select-Object -Skip 4) -join $Separator
# Rename object
Rename-Item -Path $_.FullName -NewName $NewName
}

find and delete lines without string pattern in text files

I'm trying to find out how to use powershell to find and delete lines without certain string pattern in a set of files. For example, I have the following text file:
111111
22x222
333333
44x444
This needs to be turned into:
22x222
44x444
given that the string pattern of 'x' is not in any of the other lines.
How can I issue such a command in powershell to process a bunch of text files?
thanks.
dir | foreach { $out = cat $_ | select-string x; $out | set-content $_ }
The dir command lists the files in the current directory; the foreach goes through each file; cat reads the file and pipes into select-string; select-string finds the lines that contains the specific pattern, which in this case is "x"; the result of select-string is stored in $out; and finally, $out is written to the same file with set-content.
We need the temporary variable $out because you cannot read and write the same file at the same time.
This will process all txt files from the working directory. Each file content is checked and only lines that have 'x' in them are allowed to pass on. The result is written back to the file.
Get-ChildItem *.txt | ForEach-Object{
$content = Get-Content $_.FullName | Where-Object {$_ -match 'x'}
$content | Out-File $_.FullName
}

How to remove some words from all text file in a folder by powershell?

I have a situation that I need to remove some words from all text file in a folder.
I know how to do that only in 1 file, but I need to do it automatically for all text files in that folder. I got no idea at all how to do it in powershell.
The name of the files are random.
Please help.
This is the code
$txt = get-content c:\work\test\01.i
$txt[0] = $txt[0] -replace '-'
$txt[$txt.length - 1 ] = $txt[$txt.length - 1 ] -replace '-'
$txt | set-content c:\work\test\01.i
Basicly it jsut removes a "-" from first line and last line, but i need to do this on all files in the folder.
Get-ChildItem c:\yourfolder -Filter *.txt | Foreach-Object{
... your code goes here ...
... you can access the current file name via $_.FullName ...
}
Here is a full working example:
Get-ChildItem c:\yourdirectory -Filter *.txt | Foreach-Object{
(Get-Content $_.FullName) |
Foreach-Object {$_ -replace "what you want to replace", "what to replace it with"} |
Set-Content $_.FullName
}
Now for a quick explanation:
Get-ChildItem with a Filter: gets all items ending in .txt
1st ForEach-Object: will perform the commands within the curly brackets
Get-Content $_.FullName: grabs the name of the .txt file
2nd ForEach-Object: will perform the replacement of text within the file
Set-Content $_.FullName: replaces the original file with the new file containing the changes
Important Note: -replace is working with a regular expression so if your string of text has any special characters
something like this ?
ls c:\temp\*.txt | %{ $newcontent=(gc $_) -replace "test","toto" |sc $_ }
$files = get-item c:\temp\*.txt
foreach ($file in $files){(Get-Content $file) | ForEach-Object {$_ -replace 'ur word','new word'} | Out-File $file}
I hope this helps.
Use Get-Childitem to filter for the files you want to modify. Per response to previous question "Powershell, like Windows, uses the extension of the file to determine the filetype."
Also:
You will replace ALL "-" with "" on the first and last lines, using what your example shows, IF you use this instead:
$txt[0] = $txt[0] -replace '-', ''
$txt[$txt.length - 1 ] = $txt[$txt.length - 1 ] -replace '-', ''