How to cut specific string? - powershell

I have a string with different length. I want to cut a specific word in my string.
Please help, I am new to PowerShell.
I tried this code, it's still not what I need.
$String = "C:\Users\XX\Documents\Data.txt"
$Cut = $String.Substring(22,0)
$Cut
My expectation is that I can return the word Data.

Assuming the string is always the same format (i.e. a path ending in a filename), then there are quite a few ways to do this, such as using regular expressions. Here is a slightly less conventional method:
# Define the path
$filepath = "C:\Users\XX\Documents\Data.txt"
# Create a dummy fileinfo object
$fileInfo = [System.IO.FileInfo]$filePath
# Get the file name property
$fileInfo.BaseName
Of course, you could do all of this in one step:
([System.IO.FileInfo]"C:\Users\XX\Documents\Data.txt").BaseName

If the path is an existing one, you could use
(Get-Item $String).BaseName
Otherwise
(Split-Path $String -Leaf) -Replace '\.[^\.]*$'

While in that specific example the simplest way is to use Substring(startPosition,length) to extract file name you'd probably want to use something like this:
(("C:\Users\XX\Documents\Data.txt".split("\\"))[-1].Split("."))[0]
Explanation:
("C:\Users\XX\Documents\Data.txt".split("\\"))[-1]
that part split the path by \ and returns last item (escaping it seems to be not mandatory by the way so you can use .split("\") instead of .split("\\")). From it you receive Data.txt so you have to separate name and extension. You can do this by splitting by . and choosing first element returned

There are number of ways of doing it depending upon your input -
Method 1 - Hard-coding using the sub-string function.
$String = "C:\Users\XX\Documents\Data.txt"
$Cut = $String.Substring(22,4)
$Cut
The above approach will work for a single input but will become difficult to manage for multiple inputs of different lengths.
Method 2 - Using the split method
$String = "C:\Users\XX\Documents\Data.txt"
$cut = $String.Split("\")[-1].split(".")[0]
$cut
Split method will split string into substring. The index [-1] will return the last value returned by the split method.
The second split is to return the word Data from the word Data.txt.
Method 3 - If the input is a file path
$string = Get-ChildItem $env:USERPROFILE\Desktop -File | select -First 1
$Cut = $String.BaseName
More about method 3 here.

If you can use Powershell 6 - SplitPath
#Requires -Version 6.0
Split-Path $String -LeafBase

Related

Can Powershell return complete interpolated values using convertfrom-stringdata

I have a .properties file with the following properties in them:
repository.host=hostname.com/nexus
repository.api.url=https://${repository.host}/service/rest/v1
repository.url=https://${repository.host}/repository
I am able to return the values using the following powershell function:
static [string] getProperty( [string] $property ){
$properties = "../resources/vars/$([jenkins]::PROPERTIES_FILE)"
$properties = get-content $properties | convertfrom-stringdata
return $properties.$property
}
When attempting to return the property repository.url powershell return this string: https://${repository.host}/repository/
My question is: Is it possible through features that already exist in powershell for the returned string to be https://hostname.com/nexus/repository/?
By design, for security reasons, ConvertFrom-StringData does not perform string expansion (interpolation) on its input.
Assuming you trust the input string[1], you can perform the expansion on demand, after having read the values from the file.
Note that use of ConvertFrom-StringData is problematic, as you've discovered, because the hashtable it creates invariably has unordered keys; that is, the order of the entries does not reflect the order in which the properties are defined in the file.
Therefore, processing the hashtable entries can make the on-demand expansion fail, if an out-of-order entry is processed before another entry whose value it needs for the expansion.
The solution is to roll your own ConvertFrom-StringData variant[2] that reads the properties into an ordered hashtable.
This additionally allows you to combine the read-from-file and expansion-on-demand tasks:
# Create a sample properties file.
#'
repository.host=hostname.com/nexus
repository.api.url=https://${repository.host}/service/rest/v1
repository.url=https://${repository.host}/repository
'# > sample.properties
# Parse the file and build an *ordered* hashtable from it.
$orderedHash = [ordered] #{}
switch -Regex -File sample.properties {
'^\s*#|^\s*$' { continue } # skip comment and blank lines.
default {
# Determine the key and value...
$key, $value = $_ -split '=', 2
# ... and create the entry while expanding ${...} references to previous
# entries.
$orderedHash[$key.Trim()] = $ExecutionContext.InvokeCommand.ExpandString((
$value.Trim() -replace '\$\{([^}]+)\}', '$$($$orderedHash[''$1''])'
))
}
}
# Output the result.
$orderedHash
Note the use of method $ExecutionContext.InvokeCommand.ExpandString to perform on-demand string expansion (interpolation); since this method isn't easy to discover, GitHub issue #11693 proposes that this functionality be surfaced as a proper, easily discoverable cmdlet named something like Expand-String or Expand-Template.
Note: In order to be able to use $ExecutionContext from the method of a PS custom class, you must explicitly reference it in the global scope via $global:ExecutionContext.
For more information about the regex-based -replace operator, see this answer.
The above yields (note that the input order was maintained):
Name Value
---- -----
repository.host hostname.com/nexus
repository.api.url https://hostname.com/nexus/service/rest/v1
repository.url https://hostname.com/nexus/repository
[1] Via $(), the subexpression operator, it is possible to embed arbitrary commands in the input strings.
[2] The code below does not replicate all features of ConvertFrom-String data, but it works with the sample input. While it does support skipping comment lines (those whose first non-whitespace character is a #) and blank lines, treating \ as escape characters and supporting escape sequences such as \n for a newline is not implemented.
The original solution provided by #mklement0 was very useful, and has guided me towards a more complete solution. This solution accomplishes/corrects a couple of things:
The ability to create the hashtable from a file source.
The ability to access the $ExecutionContext variable from within a class method, using the $global: scope.
The ability to thoroughly parse all keys within the hashtable.
static [string] getProperties ( [string] $file, [string] $property ){
$properties = get-content $file -raw | convertfrom-stringdata
while ( $properties.values -match '\$\{([^}]+)\}' ){
foreach ($key in #($properties.Keys)) {
$properties[$key] = $global:ExecutionContext.InvokeCommand.ExpandString( ($properties[$key] -replace '\$\{([^}]+)\}', '$$($$properties[''$1''])') )
}
}
return $properties[$property]
}
Note: When the while loop is not present and searching matches of ${*}, any given returned value may not be completely interpolated or expanded. As an example without the while loop present output from a file may look like this:
/nexus
${nexus.protocol}://${nexus.hostname}:${nexus.port}${nexus.context}
${nexus.protocol}://${nexus.hostname}:${nexus.port}${nexus.context}/repository/installers/com/amazon/java/8.0.252/java-1.8.0-amazon-corretto-devel-1.8.0_252.b09-1.x86_64.rpm
${nexus.protocol}://${nexus.hostname}:${nexus.port}${nexus.context}
${nexus.protocol}://${nexus.hostname}:${nexus.port}${nexus.context}/repository/installers/com/oracle/tuxedo/12.1.3.0.0/p30596495_121300_Linux-x86-64.zip
443
https://hostname.com:443/nexus
https://hostname.com:443/nexus/repository/installers/com/oracle/java/jdk/8u251/jdk-8u251-linux-x64.rpm
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
hostname.com
https
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
And if you were to run the same script again (still without the while loop) would look like this:
hostname.com
https://hostname.com:443/nexus
/nexus
https://hostname.com:443/nexus
https://hostname.com:443/nexus
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
https://${nexus.hostname}:443/nexus/repository/installers/com/oracle/java/jdk/8u251/jdk-8u251-linux-x64.rpm
https://hostname.com:443/nexus/repository/installers/com/oracle/tuxedo/12.1.3.0.0/p30596495_121300_Linux-x86-64.zip
https://hostname.com:443/nexus/repository/installers/com/amazon/java/8.0.252/java-1.8.0-amazon-corretto-devel-1.8.0_252.b09-1.x86_64.rpm
443
https
https://${nexus.hostname}:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
The reason for the sometimes incompletely interpolated/expanded strings is because hashtables are naturally unordered. With the introduction of the while loop, results will not be returned until all interpolated/expanded strings are resolved.
The official output would look as such:
hostname.com
https://hostname.com:443/nexus
/nexus
https://hostname.com:443/nexus
https://hostname.com:443/nexus
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip
https://hostname.com:443/nexus/repository/installers/com/oracle/java/jdk/8u251/jdk-8u251-linux-x64.rpm
https://hostname.com:443/nexus/repository/installers/com/oracle/tuxedo/12.1.3.0.0/p30596495_121300_Linux-x86-64.zip
https://hostname.com:443/nexus/repository/installers/com/amazon/java/8.0.252/java-1.8.0-amazon-corretto-devel-1.8.0_252.b09-1.x86_64.rpm
443
https
https://hostname.com:443/nexus/repository/installers/com/oracle/weblogic/12.2.1.3.0/p30965714_122130_Generic.zip

How to find the positions of all instances of a string in a specific line of a txt file?

Say that I have a .txt file with lines of multiple dates/times:
5/5/2020 5:45:45 AM
5/10/2020 12:30:03 PM
And I want to find the position of all slashes in one line, then move on to the next.
So for the first line I would want it to return the value:
1 3
And for the second line I would want:
1 4
How would I go about doing this?
I currently have:
$firstslashpos = Get-Content .\Documents\LoggedDates.txt | ForEach-Object{
$_.IndexOf("/")}
But that gives me only the first "/" on each line, and gives me that result for all lines at once. I need it to loop where I can figure out the space between each "/" for each line.
Sorry if I worded this badly.
You can indeed use the String.IndexOf() method for this!
function Find-SubstringIndex
{
param(
[string]$InputString,
[string]$Substring
)
$indices = #()
# start at position zero
$offset = 0
# Keep calling IndexOf() to find the next occurrence of the substring
# stop when IndexOf() returns -1
while(($i = $InputString.IndexOf($Substring, $offset)) -ne -1){
# Keep track of the index at which the substring was found
$indices += $i
# Update the offset, we'll want to start searching for the next index _after_ this one
$offset = $i + $Substring.Length
}
}
Now you can do:
Get-Content listOfDates.txt |ForEach-Object {
$indices = Find-SubstringIndex -InputString $_ -Substring '/'
Write-Host "Found slash at indices: $($indices -join ',')"
}
An concise solution is to use [regex]::Matches(), which finds all matches of a given regular expression in a given string and returns a collection of match objects that also indicate the index (character position) of each match:
# Create a sample file.
#'
5/5/2020 5:45:45 AM
5/10/2020 12:30:03 PM
'# > sample.txt
Get-Content sample.txt | ForEach-Object {
# Get the indices of all '/' instances.
$indices = [regex]::Matches($_, '/').Index
# Output them as a list (string), separated with spaces.
"$indices"
}
The above yields:
1 3
1 4
Note:
Input lines that contain no / instances at all will result in empty lines.
If, rather than strings, you want to output the indices as arrays (collections), use
, [regex]::Matches($_, '/').Index as the only statement in the ForEach-Object script block; the unary form of ,, the array constructor operator ensures (by way of a transient aux. array) that the collection returned by the method call is output as a whole. If you omit the , , the indices are output one by one, resulting in a flat array when collected in a variable.

How to get the index of the last occurence of a char in PowerShell string?

I want to get the index of the last "\" occurrence in order to trim the "Activity" word and keep it, from following string in PowerShell:
$string = "C:\cmb_Trops\TAX\Auto\Activity"
I'm converting the code from VBScript to PowerShell and in VB there's this solution :
Right(string, Len(string) - InStrRev(string, "\"))
Using Right and InStrRev functions which makes the life more easier. Unfortunately I didn't find anything like it in PowerShell. Can't find any option to scan from the end of the string.
$String.Split("\")[-1]
Or if $String is actually a real path, you might consider:
Split-Path $String -Leaf
$string = "C:\cmb_Trops\TAX\Auto\Activity"
$string = $string.Substring($string.lastIndexOf('\') + 1)
echo $string
Check out:
https://community.spiceworks.com/topic/1330191-powershell-remove-all-text-after-last-instance-of

Split a string with powershell to get the first and last element

If you do: git describe --long
you get: 0.3.1-15-g3b885c5
Thats the meaning of the above string:
Tag-CommitDistance-CommitId (http://git-scm.com/docs/git-describe)
How would you split the string to get the first (Tag) and last (CommitId) element?
By using String.split() with the count parameter to manage dashes in the commitid:
$x = "0.3.1-15-g3b885c5"
$tag = $x.split("-",3)[0]
$commitid = $x.split("-",3)[-1]
Note: This answer focuses on improving on the split-into-tokens-by-- approach from Richard's helpful answer, though note that that approach isn't fully robust, because git tag names may themselves contain - characters, so you cannot blindly assume that the first - instance ends the tag name.
To account for that, use Richard's robust solution instead.
Just to offer a more PowerShell-idiomatic variant:
# Stores '0.3.1' in $tag, and 'g3b885c5' in $commitId
$tag, $commitId = ('0.3.1-15-g3b885c5' -split '-')[0, -1]
PowerShell's -split operator is used to split the input string into an array of tokens by separator -
While the [string] type's .Split() method would be sufficient here, -split offers many advantages in general.
[0, -1] extracts the first (0) and last (-1) element from the array returned by -split and returns them as a 2-element array.
$tag, $commitId = is a destructuring multi-assignment that assigns the elements of the resulting 2-element array to a variable each.
I can't recall if dashes are allowed in tags, so I'll assume they are, but will not appear in the last two fields.
Thus:
if ("0.3.1-15-g3b885c5" -match '(.*)-\d+-([^-]+)') {
$tag = $Matches[1];
$commitId = $Matches[2]
}

Remove first and last three character of a word with powershell

I have a list of users in a text file who's names are in the following format: xn-tsai-01.
How do I script to remove the xn- KEEP THIS -01 so the output is like: tsai
I know how to do this in bash but not too familiar with powershell.
Thanks in advance!
Why not use Substring method. If you will always trim the first three characters, you can do the following assuming the variable is a string type.
$string = xn-tsai-01
$string.Substring(3)
Here is a quick way to do it using regex:
'xn-tsai-01' -replace '.*?-(.*)-.*','$1'
Example with a list:
(Get-Content list.txt) -Replace '.*?-(.*)-.*','$1'
You can use the .NET string method IndexOf("-") to find the first, and LastIndexOf("-") to find the last occurrence of "-" within the string.
Use these indexes with Substring() to remove the unnecessary parts:
function Clean-Username {
param($Name)
$FirstDash = $Name.IndexOf("-") + 1
$LastDash = $Name.LastIndexOf("-")
return $Name.Substring( $f, $l - $f )
}
PS C:\> Clean-UserName -Name "xn-tsai-01"
tsai
Boe's example is probably going to be the most efficient.
Another way is to use the split() method if they're in a uniform format.
Get-Content .\list.txt | % { ($_.Split('-'))[1] }
% is an alias for ForEach