i want to read a string from a line. The line is
ERROR: file'C:\Program Files (x86)\movies\action\Theincrediblehulk.mp3' is missing
I want only
Theincrediblehulk.mp3
the code i use to get only that string is
Select-String tt.txt -pattern [regex]"[A-Za-z]+\.mp3" -AllMatches | % { $_.Matches } | % { $_.Value }
but it is giving me the output as
crediblehulk.mp3
first two or three words is missing.
Please suggest a better way to solve this. i use [A-Za-z] because name is dynamic.
Start by grabbing everything in between the quotes:
$Filepaths = Select-String tt.txt -pattern "'([^']+)'" -AllMatches | % { $_.Matches } | % { $_.Groups[1].Value }
Now you can use either Split-Path -Leaf to grab the file name:
$Filenames = $Filepaths |Split-Path -Leaf
or Path.GetFileName():
$Filenames = $Filepaths |% { [System.IO.Path]::GetFileName($_) }
Mathias R. Jessen's answer is a better solution, however this answer explains why the original code isn't working as expected.
I assume by your [regex] at the beginning, you're trying to tell powershell to convert your string to a regex object. However powershell is actually interpreting your argument as
-pattern '[regex]"[A-Za-z]+\.mp3"'
If you do want to explicitly treat your string as a regex object, you'll need to wrap the value in parentheses
-pattern ([regex]"[A-Za-z]+\.mp3")
Although the regex cast is not necessary, so the string alone is sufficient.
-pattern '[A-Za-z]+\.mp3'
Related
I am trying to replace every instance of a string within a directory. However my code is not replacing anything.
What I have so far:
Test Folder contains multiple files and folders containing content that I need to change.
The folders contain .txt documents, the .txt documents contain strings like this: Content reference="../../../PartOfPath/EN/EndofPath/Caution.txt" that i need to change into this: Content reference="../../../PartOfPath/FR/EndofPath/Caution.txt"
Before this question comes up, yes it has to be done this way, as there are other similar strings that I don't want to edit. So I cannot just replace all instances of EN with FR.
$DirectoryPath = "C:\TestFolder"
$Parts =#(
#{PartOne="/PartOfPath";PartTwo="EndofPath/Caution.txt"},
#{PartOne="/OtherPartOfPath";PartTwo="EndofPath/Note.txt"},
#{PartOne="/ThirdPartOfPath";PartTwo="OtherEndofPath/Warning.txt"}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
Get-ChildItem $DirectoryPath | ForEach {
foreach($n in $Parts){
[string]$PartOne = $n.PartOne
[string]$PartTwo = $n.PartTwo
$ReplaceThis = "$PartOne/EN/$PartTwo"
$WithThis = "$PartOne/FR/$PartTwo"
(Get-Content $_) | ForEach {$_ -Replace $ReplaceThis, $WithThis} | Set-Content $_
}
}
The code will run and overwrite files, however no edits will have been made.
While troubleshooting I came across this potential cause:
This test worked:
$FilePath = "C:\TestFolder\Test.txt"
$ReplaceThis ="/PartOfPath/EN/Notes/Note.txt"
$WithThis = "/PartOfPath/FR/Notes/Note.txt"
(Get-Content -Path $FilePath) -replace $ReplaceThis, $WithThis | Set-Content $FilePath
But this test did not
$FilePath = "C:\TestFolder\Test.txt"
foreach($n in $Parts){
[string]$PartOne = $n.PartOne
[string]$PartTwo = $n.PartTwo
[string]$ReplaceThis = "$PartOne/EN/$PartTwo"
[string]$WithThis = "$PartOne/FR/$PartTwo"
(Get-Content -Path $FilePath) -replace $ReplaceThis, $WithThis | Set-Content $FilePath
}
If you can help me understand what is wrong here I would greatly appreciate it.
Thanks to #TessellatingHeckler 's comments I revised my code and found this solution:
$DirectoryPath = "C:\TestFolder"
$Parts =#(
#{PartOne="/PartOfPath";PartTwo="EndofPath/Caution.txt"},
#{PartOne="/OtherPartOfPath";PartTwo="EndofPath/Note.txt"},
#{PartOne="/ThirdPartOfPath";PartTwo="OtherEndofPath/Warning.txt"}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
Get-ChildItem $LanguageFolderPath -Filter "*.txt" -Recurse | ForEach {
foreach($n in $Parts){
[string]$PartOne = $n.PartOne
[string]$PartTwo = $n.PartTwo
$ReplaceThis = "$PartOne/EN/$PartTwo"
$WithThis = "$PartOne/FR/$PartTwo"
(Get-Content $_) | ForEach {$_.Replace($ReplaceThis, $WithThis)} | Set-Content $_
}
}
There were two problems:
Replace was not working as I intended, so I had to use .replace instead
The original Get-ChildItem was not returning any values and had to be replaced with the above version.
PowerShell's -replace operator is regex-based and case-insensitive by default:
To perform literal replacements, \-escape metacharacters in the pattern or call [regex]::Escape().
By contrast, the [string] type's .Replace() method performs literal replacement and is case-sensitive, invariably in Windows PowerShell, by default in PowerShell (Core) 7+ (see this answer for more information).
Therefore:
As TessellatingHeckler points out, given that your search strings seem to contain no regex metacharacters (such as . or \) that would require escaping, there is no obvious reason why your original approach didn't work.
Given that you're looking for literal substring replacements, the [string] type's .Replace() is generally the simpler and faster option if case-SENSITIVITY is desired / acceptable (invariably so in Windows PowerShell; as noted, in PowerShell (Core) 7+, you have the option of making .Replace() case-insensitive too).
However, since you need to perform multiple replacements, a more concise, single-pass -replace solution is possible (though whether it actually performs better would have to be tested; if you need case-sensitivity, use -creplace in lieu of -replace):
$oldLang = 'EN'
$newLang = 'FR'
$regex = #(
"(?<prefix>/PartOfPath/)$oldLang(?<suffix>/EndofPath/Caution.txt)",
"(?<prefix>/OtherPartOfPath/)$oldLang(?<suffix>/EndofPath/Note.txt)",
"(?<prefix>/ThirdPartOfPath/)$oldLang(?<suffix>/OtherEndofPath/Warning.txt)"
) -join '|'
Get-ChildItem C:\TestFolder\Test.txt -Filter *.txt -Recurse | ForEach-Object {
($_ |Get-Content -Raw) -replace $regex, "`${prefix}$newLang`${suffix}" |
Set-Content -LiteralPath $_.FullName
}
See this regex101.com page for an explanation of the regex and the ability to experiment with it.
The expression used as the replacement operand, "`${prefix}$newLang`${suffix}", mixes PowerShell's up-front string interpolation ($newLang, which could also be written as ${newLang}) with placeholders referring to the named capture groups (e.g. (?<prefix>...)) in the regex, which only coincidentally use the same notation as PowerShell variables (though enclosing the name in {...} is required; also, here the $ chars. must be `-escaped to prevent PowerShell's string interpolation from interpreting them); see this answer for background information.
Note the use of -Raw with Get-Content, which reads a text file as a whole into memory, as a single, multi-line string. Given that you don't need line-by-line processing in this case, this greatly speeds up the processing of a given file.
As a general tip: you may need to use the -Encoding parameter with Set-Content to ensure the desired character encoding, given that PowerShell never preserves a file's original coding when reading it. By default, you'll get ANSI-encoded files in Windows PowerShell, and BOM-less UTF-8 files in PowerShell (Core) 7+.
I want to use the $_ in a Pattern in Powershell. I have the following script but it doesn't work
Get-Content ..\myfile.txt | ForEach-Object {
$counter=(gc *.log | Select-String -Pattern '$_\/Directory\/Cars\/Sign.jpg')
}
If I run the script, in lines of the myfile.txt which I know there is some ocurrences according to the Pattern it finds 0, so how I must write the $_ in the pattern?.
Thanks so much
The reason PowerShell isn't expanding $_ is because you're using a verbatim string literal (a string literal defined using ' single-quotes).
For an expandable string literal, you need to use " double-quotes - PowerShell will then attempt to expand variable expressions inside the string:
Get-Content ..\myfile.txt | ForEach-Objet {
$counter=(gc *.log | Select-String -Pattern "${_}/Directory/Cars/Sign\.jpg")
}
Note: ${_} is the same variable as $_.
The explicit {} qualifiers prevents PowerShell from interpreting something that isn't actually part of the variable name as such. Let's say your pattern looked like "$_Sign\.jpg", PowerShell would attempt to resolve the token $_Sign instead of just $_, whereas "${_}Sign\.jpg" makes it obvious to PowerShell's parser where the variable expression ends.
Depending on what myfile.txt contains, you might want to escape the input string:
Get-Content ..\myfile.txt | ForEach-Object {
$counter=(gc *.log | Select-String -Pattern "$([regex]::Escape($_))/Directory/Cars/Sign\.jpg")
}
For further reading on these topics, peruse the about_Quoting_Rules and about_Regular_Expressions help topics
I'm trying to make using of String.Substring() to replace every string with its substring from a certain position. I'm having a hard time figuring out the right syntax for this.
$dirs = Get-ChildItem -Recurse $path | Format-Table -AutoSize -HideTableHeaders -Property #{n='Mode';e={$_.Mode};width=50}, #{n='LastWriteTime';e={$_.LastWriteTime};width=50}, #{n='Length';e={$_.Length};width=50}, #{n='Name';e={$_.FullName -replace "(.:.*)", "*($(str($($_.FullName)).Substring(4)))*"}} | Out-String -Width 40960
I'm referring to the following expression
e={$_.FullName -replace "(.:.*)", "*($(str($($_.FullName)).Substring(4)))*"}}
The substring from the 4th character isn't replacing the Full Name of the path.
The paths in question are longer than 4 characters.
The output is just empty for the Full Name when I run the script.
Can someone please help me out with the syntax
EDIT
The unaltered list of strings (as Get-ChildItem recurses) would be
D:\this\is\where\it\starts
D:\this\is\where\it\starts\dir1\file1
D:\this\is\where\it\starts\dir1\file2
D:\this\is\where\it\starts\dir1\file3
D:\this\is\where\it\starts\dir1\dir2\file1
The $_.FullName will therefore take on the value of each of the strings listed above.
Given an input like D:\this\is or D:\this\is\where, then I'm computing the length of this input (including the delimiter \) and then replacing $_.FullName with a substring beginning from the nth position where n is the length of the input.
If input is D:\this\is, then length is 10.
Expected output is
\where\it\starts
\where\it\starts\dir1\file1
\where\it\starts\dir1\file2
\where\it\starts\dir1\file3
\it\starts\dir1\dir2\file1
If you want to remove a particular prefix from a string you can do so like this:
$prefix = 'D:\this\is'
...
$_.FullName -replace ('^' + [regex]::Escape($prefix))
To remove a prefix of a given length you can do something like this:
$len = 4
...
$_.FullName -replace "^.{$len}"
When having trouble, simplify:
This function will do what you are apparently trying to accomplish:
Function Remove-Parent {
param(
[string]$Path,
[string]$Parent)
$len = $Parent.length
$Path.SubString($Len)
}
The following is not the way you likely would use it but does demonstrate that the function returns the expected results:
#'
D:\this\is\where\it\starts
D:\this\is\where\it\starts\dir1\file1
D:\this\is\where\it\starts\dir1\file2
D:\this\is\where\it\starts\dir1\file3
D:\this\is\where\it\starts\dir1\dir2\file1
'# -split "`n" | ForEach-Object { Remove-Parent $_ 'D:\This\Is' }
# Outputs
\where\it\starts
\where\it\starts\dir1\file1
\where\it\starts\dir1\file2
\where\it\starts\dir1\file3
\where\it\starts\dir1\dir2\file1
Just call the function with the current path ($_.fullname) and the "prefix" you are expecting to remove.
The function above is doing this strictly on 'length' but you could easily adapt it to match the actual string with either a string replace or a regex replace.
Function Remove-Parent {
param(
[string]$Path,
[string]$Parent
)
$remove = [regex]::Escape($Parent)
$Path -replace "^$remove"
}
The output was the same as above.
I'm trying to write a powershell cmdlet to find multiple words in lines in file. Example. I need to parse "word1", "word2", "word3" are in the same line of a file. I'm doing something wrong because I tried this with no success:
(gci -File -Filter FileName | Select-String -SimpleMatch word1, word2,word3) > outputFileName.txt
where FileName = name of file, outputFileName = generated file from my search of the three words. Thank you.
Select-String doesn't have any combination operator that I can think of. If your words were always in that order, then you could do -Pattern 'word1.*word2.*word3' as your match, but if they could be in any order that would get complex very quickly. Instead, I'd look at
.. | Select-String 'word1' | Select-String 'word2' | Select-String 'Word3'
so, all the lines which match word1. Out of those, the ones which match word2 somewhere. Out of that even smaller result, the ones which also match word3.
try this:
$wordlist=#("word1", "word2", "word3")
Get-ChildItem "c:\temp\" -file | %{$currentfile=$_.FullName; Get-Content $_.FullName |
%{
$founded=$true
foreach ($item in $wordlist)
{
if (!$_.Contains($item))
{
$founded=$false
break
}
}
if ($founded)
{
$currentfile
}
}
}
I am trying to build a PowerShell script that iterates through a list of files and searches and removes a match, not having much luck, here is my script
$path = "D:\Test\"
$filter = "*.txt"
$files = Get-ChildItem -path $path -filter $filter
foreach ($item in $files)
{
$search = Get-content -path $path$item
$search| select-string -pattern "T|"
}
At the moment the script is just returning the whole content of the file and not the select string.
Basically each file in the folder will have a trailer record at the end i.e. T|1410 I need to iterate through all the files and delete the last line, some of these files will be 200mb+ can someone guide me please.
I've edited my script and now I am using the following method.
$path = "D:\Test\"
$filter = "*.txt"
$files = Get-ChildItem -path $path -filter $filter
foreach ($item in $files)
{
$search = Get-content $path$item
($search)| ForEach-Object { $_ -replace 'T\|[0-9]*', '' } | Set-Content $path$item
}
I am using Powershell v.2
However, this is adding a new empty line to my end of file as well as leaving the replace empty, how can I avoid this as well as starting the search from the bottom
-pattern "T|"
That pattern matches a "T" or nothing. But there is nothing between every pair of characters in any string. To avoid the usual regular expression handling of | as an alternates separator, use a backslash to match a literal |:
-pattern "T\|"
Alternately, use Select-String's -SimpleMatch switch to stop the argument to -Pattern being treated as a regular expression.
As Richard mentioned, you have to escape the | character.
You could also use the regex::escape function for that:
[regex]::Escape("T|")
Aside from escaping the characters the other option you have available is the -SimpleMatch switch. From TechNet
Uses a simple match rather than a regular expression match. In a simple match, Select-String searches the input for the text in the Pattern parameter. It does not interpret the value of the Pattern parameter as a regular expression statement.
If you don't want to have to worry about escaping the characters and are not using regex this would be the way to go.
$search | select-string -pattern "T|" -SimpleMatch