Powershell replace first instance of word starting on new line - powershell

So lets say I have a multi line string as below.
#abc
abc def
abc
I want to only replace the first instance of abc that starts on a new line with xyz while ignoring any whitespaces that might precede it (like in the above example)
So my replaced string should read
#abc
xyz def
abc
Not very good at regex so would appreciate suggestions. Thanks!

To do that, you need a regular expression that anchors to the beginning of a line, allows for multiple leading whitespaces and uses a word boundary to make sure you do not replace part of a larger string.
$multilineText = #"
#abc
abc def
abc
"#
$toReplace = 'abc'
$replaceWith = 'xyz'
# create the regex string.
# Because the example `abc` in real life could contain characters that have special meaning in regex,
# you need to escape these characters in the `$toReplace` string.
$regexReplace = '(?m)^(\s*){0}\b' -f [regex]::Escape($toReplace)
# do the replacement and capture the result to write to a new file perhaps?
$result = ([regex]$regexReplace).Replace($multilineText, "`$1$replaceWith", 1)
# show on screen
$result
The above works Case-Sensitive, but if you do not want that, simply change (?m) into (?mi) in the $regexReplace definition.
Output:
#abc
xyz def
abc
Regex details:
(?m) Match the remainder of the regex with the options: ^ and $ match at line breaks (m)
^ Assert position at the beginning of a line (at beginning of the string or after a line break character)
( Match the regular expression below and capture its match into backreference number 1
\s Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
){0}
\b Assert position at a word boundary
Special Characters in Regex
Char
Description
Meaning
\
Backslash
Used to escape a special character
^
Caret
Beginning of a string
$
Dollar sign
End of a string
.
Period or dot
Matches any single character
|
Vertical bar or pipe symbol
Matches previous OR next character/group
?
Question mark
Match zero or one of the previous
*
Asterisk or star
Match zero, one or more of the previous
+
Plus sign
Match one or more of the previous
( )
Opening and closing parenthesis
Group characters
[ ]
Opening and closing square bracket
Matches a range of characters
{ }
Opening and closing curly brace
Matches a specified number of occurrences of the previous

The 1 just replaces the first instance of 'abc' with 'xyz' within a string.
Write-Host "Replace Example One" -ForegroundColor Yellow -BackgroundColor DarkGreen
$test = " abc def abc "
[regex]$pattern = "abc"
$pattern.replace($test, "xyz", 1)
Write-Host "Replace Example Two" -ForegroundColor Green -BackgroundColor Blue
$test = Get-Content "c:\test\text.txt"
[regex]$pattern = "abc"
$x = $pattern.replace($test, "xyz", 1)
Write-Host $x
Write-Host "Replace Example Three" -ForegroundColor White -BackgroundColor Red
$multilineText = #"
#abc
abc def
abc
"#
[regex]$pattern = "abc"
$y = $pattern.replace($multilineText, "xyz", 1)
Write-Host $y

Related

Trim Powershell OutPut

Requirement is to trim the Output. Retain only the output quoted within double quotes from Name and remove/avoid the earlier lines/characters
From:
$R.Output = \\GBVServer1\root\cimv2:Win32_Group.Domain="Contoso",Name="Domain Users"
$R.Output = \\GBVServer1\root\cimv2:Win32_SystemAccount.Domain="GBVServer1",Name="INTERACTIVE"
To:
$R.Output = Domain Users
$R.Output = INTERACTIVE
Could somebody assist with the powershell switch to be used?
You can do this with regex to capture only the Name part between the quotes for these strings:
$regex = [regex]'(?i)Name="([^,]+)"'
$string = '\\GBVServer1\root\cimv2:Win32_Group.Domain="Contoso",Name="Domain Users"'
$R.Output = $regex.Match($string).Groups[1].Value # --> Domain Users
$string = '\\GBVServer1\root\cimv2:Win32_SystemAccount.Domain="GBVServer1",Name="INTERACTIVE"'
$R.Output = $regex.Match($string).Groups[1].Value # --> INTERACTIVE
Regex details:
Name=" Match the characters “Name="” literally
( Match the regular expression below and capture its match into backreference number 1
[^,] Match any character that is NOT a “,”
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
" Match the character “"” literally
The (?i) makes the match case-insensitive

Replace string with Powershell

I have a .properties file in which I want to replace the string Compass with BBB. My question is : I want to extract string which is belong
name , JDBC/ , ds_name = '' , java.lang.String then I will update with a new one. BTW, data source name is not fixed its dynamic variable. Just I have written it as sample string.
I have tried the following PowerShell code:
$DName = read-host -prompt "Please Enter Database Name"
ForEach ($client in (Get-Content Clients.txt)) {
(Get-Content "\\$client\D$\Runtime\run.properties") -replace "$old database name which is extract","$DName" |
Out-File "\\$client\D$\Runtime\run.properties"
}
run.properties:
dsid = AdminTask.createDatasource(provider_id, '[-name Compass -jndiName jdbc/Compass
-dataStoreHelperClassName com.ibm.websphere.rsadapter.MicrosoftSQLServerDataStoreHelper
-componentManagedAuthenticationAlias TEMP-HRZEMM01Node01/PlatformDataSource -containerManagedPersistence true
-xaRecoveryAuthAlias TEMP-HRZEMM01Node01/PlatformDataSource -configureResourceProperties [[databaseName java.lang.String Compass] [portNumber java.lang.Integer 1433] [serverName java.lang.String SQLSVR1]]]')
AdminConfig.create('MappingModule', dsid , '[[authDataAlias TEMP-HRZEMM01Node01/PlatformDataSource] [mappingConfigAlias ""]]')
ds_name = 'Compass' #Name copied from your question, update if required
If I understand the question correctly, you would like to first find the database name (which can be anything, Compass is just an example) stored in the .properties file and if found replace that by a value entered in the console.
In that case, I think this should do it:
$newDbName = Read-Host -prompt "Please Enter Database Name"
$clientFile = "Clients.txt"
ForEach ($client in (Get-Content $clientFile)) {
$content = Get-Content "\\$client\D$\Runtime\run.properties" -Raw
# see if we can extract the database name from the file
if ($content -match '(?:-name\s+|jdbc/|databaseName java\.lang\.String\s+|ds_name = '')(?<dbname>[^\s''\]]+)') {
$oldDbName = $matches['dbname']
Write-Host "Replacing '$oldDbName' with '$newDbName' for client '$client'"
($content -replace $oldDbName, $newDbName) |
Out-File "\\$client\D$\Runtime\run.properties"
}
else {
Write-Warning "Could not parse the old database name from '\\$client\D$\Runtime\run.properties'.."
}
}
Regex explanation
(?: Match the regular expression below
Match either the regular expression below (attempting the next alternative only if this one fails)
-name Match the characters “-name” literally
\s Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
| Or match regular expression number 2 below (attempting the next alternative only if this one fails)
jdbc/ Match the characters “jdbc/” literally
| Or match regular expression number 3 below (attempting the next alternative only if this one fails)
databaseName\ java Match the characters “databaseName java” literally
\. Match the character “.” literally
lang Match the characters “lang” literally
\. Match the character “.” literally
String Match the characters “String” literally
\s Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
| Or match regular expression number 4 below (the entire group fails if this one fails to match)
ds_name\ =\ ' Match the characters “ds_name = '” literally
)
(?<dbname> Match the regular expression below and capture its match into backreference with name “dbname”
[^\s'\]] Match a single character NOT present in the list below
A whitespace character (spaces, tabs, line breaks, etc.)
The character “'”
A ] character
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)

Using PowerShell To Count Sentences In A File

I am having an issue with my PowerShell Program counting the number of sentences in a file I am using. I am using the following code:
foreach ($Sentence in (Get-Content file))
{
$i = $Sentence.Split("?")
$n = $Sentence.Split(".")
$Sentences += $i.Length
$Sentences += $n.Length
}
The total number of sentences I should get is 61 but I am getting 71, could someone please help me out with this? I have Sentences set to zero as well.
Thanks
foreach ($Sentence in (Get-Content file))
{
$i = $Sentence.Split("[?\.]")
$Sentences = $i.Length
}
I edited your code a bit.
The . that you were using needs to be escaped, otherwise Powershell recognises it as a Regex dotall expression, which means "any character"
So you should split the string on "[?\.]" or similar.
When counting sentences, what you are looking for is where each sentence ends. Splitting, though, returns a collection of sentence fragments around those end characters, with the ends themselves represented by the gap between elements. Therefore, the number of sentences will equal the number of gaps, which is one less the number of fragments in the split result.
Of course, as Keith Hill pointed out in a comment above, the actual splitting is unnecessary when you can count the ends directly.
foreach( $Sentence in (Get-Content test.txt) ) {
# Split at every occurrence of '.' and '?', and count the gaps.
$Split = $Sentence.Split( '.?' )
$SplitSentences += $Split.Count - 1
# Count every occurrence of '.' and '?'.
$Ends = [char[]]$Sentence -match '[.?]'
$CountedSentences += $Ends.Count
}
Contents of test.txt file:
Is this a sentence? This is a
sentence. Is this a sentence?
This is a sentence. Is this a
very long sentence that spans
multiple lines?
Also, to clarify on the remarks to Vasili's answer: the PowerShell -split operator interprets a string as a regular expression by default, while the .NET Split method only works with literal string values.
For example:
'Unclosed [bracket?' -split '[?]' will treat [?] as a regular expression character class and match the ? character, returning the two strings 'Unclosed [bracket' and ''
'Unclosed [bracket?'.Split( '[?]' ) will call the Split(char[]) overload and match each [, ?, and ] character, returning the three strings 'Unclosed ', 'bracket', and ''

Perl - partial pattern matching in a sequence of letters

I am trying to find a pattern using perl. But I am only interested with the beginning and the end of the pattern. To be more specific I have a sequence of letters and I would like to see if the following pattern exists. There are 23 characters. And I'm only interested in the beginning and the end of the sequence.
For example I would like to extract anything that starts with ab and ends with zt. There is always
So it can be
abaaaaaaaaaaaaaaaaaaazt
So that it detects this match
but not
abaaaaaaaaaaaaaaaaaaazz
So far I tried
if ($line =~ /ab[*]zt/) {
print "found pattern ";
}
thanks
* is a quantifier and meta character. Inside a character class bracket [ .. ] it just means a literal asterisk. You are probably thinking of .* which is a wildcard followed by the quantifier.
Matching entire string, e.g. "abaazt".
/^ab.*zt$/
Note the anchors ^ and $, and the wildcard character . followed by the zero or more * quantifier.
Match substrings inside another string, e.g. "a b abaazt c d"
/\bab\S*zt\b/
Using word boundary \b to denote beginning and end instead of anchors. You can also be more specific:
/(?<!\S)ab\S*zt(?!\S)/
Using a double negation to assert that no non-whitespace characters follow or precede the target text.
It is also possible to use the substr function
if (substr($string, 0, 2) eq "ab" and substr($string, -2) eq "zt")
You mention that the string is 23 characters, and if that is a fixed length, you can get even more specific, for example
/^ab.{19}zt$/
Which matches exactly 19 wildcards. The syntax for the {} quantifier is {min, max}, and any value left blank means infinite, i.e. {1,} is the same as + and {0,} is the same as *, meaning one/zero or more matches (respectively).
Just a * by itself wont match anything (except a literal *), if you want to match anything you need to use .*.
if ($line =~ /^ab.*zt$/) {
print "found pattern ";
}
If you really want to capture the match, wrap the whole pattern in a capture group:
if (my ($string) = $line =~ /^(ab.*zt)$/) {
print "found pattern $string";
}

perl one liner to search for a pattern

I have a text file like
Hi
how are you
<blank>
<blank>
abcd
<blank>
defgh
opqr
<blank>
I want to print all lines that have the pattern like "some text"blankblank"some text"
like
how are you
<blank>
<blank>
abcd
I am thinking about using join and then search for the pattern. But I am not sure how to do it. (By blank I mean empty line)
Blank line: /^(?:(?!\n)\s)*\n/m
Non-blank line: /^.*\S.*\n/m
So you want to print all instances of:
/
^
(?:
.*\S.*\n
(?: (?:(?!\n)\s)*\n ){2}
)+
.*\S.*\n
/mx
As a lone liner:
perl -0777ne'print /^(?:.*\S.*\n(?:(?:(?!\n)\s)*\n){2})+.*\S.*\n/mg' file
If all your blank lines contain no whitespace, you can simplify some:
Blank line: /^\n/m
Non-blank line: /^.+\n/m
perl -0777ne'print /^(?:.+\n\n\n)+.+\n/mg' file
Perhaps I'm not understanding the question. What I think you're asking is how you can match 2 consecutive lines that have the same text ("some text") and print those.
to do that you could do something like this
assume that the file is stored as a string in $file
print "$1\n$1" while ($file =~ /(.*)(?=\n\1(?:\n|$))/mg);
.* = matches anything, grabs up as much is it can
() = capturing group, stores .* to $1 in this case
(?= ... ) = look ahead, so that that part of the string can be used in the next match
\1 = whatever was captured in the first capturing group (i.e $1)
(?: ... ) = non-capturing group