1

Let's say I have the following string dog.puppy/cat.kitten/bear.cub. I want to get bear only. I can do this by executing the following:

$str = "dog.puppy/cat.kitten/bear.cub"
$str = $str.split('/')[2]
$str = $str.split('.')[0]

Am I able to get bear using one line using piping? This doesn't work, but it would be something like this:

$str = "dog.puppy/cat.kitten/bear.cub"
$str = $str.split('/')[2] | $_.split('.')[0]
jarrad_obrien
  • 391
  • 1
  • 4
  • 15

3 Answers3

4

Am I able to get "bear" using one line using piping?

With strings already stored in memory, there is no good reason to use the pipeline - it will only slow things down.

Instead, use PowerShell's operators, which are faster than using a pipeline and generally more flexible than the similarly named .NET [string] type's methods, because they operate on regexes (regular expressions) and can also operate on entire arrays:

PS> ('dog.puppy/cat.kitten/bear.cub' -split '[/.]')[-2]
bear

That is, split the input string by either literal / or literal . (using a character set, [...]), and return the penultimate (second to last) ([-2]) token.

See this answer for why you should generally prefer the -split operator to the String.Split() method, for instance.
The same applies analogously to preferring the -replace operator to the String.Replace() method.


It's easy to chain these operators:

PS> ('dog.puppy/cat.kitten/bearOneTwoThree.cub' -split '[/.]')[-2] -csplit '(?=\p{Lu})'
bear
One
Two
Three

That is, return the penultimate token and split it case-sensitively (using the -csplit variation of the -split operator) whenever an uppercase letter (\p{Lu}) starts, using a positive look-ahead assertion, (?=...).

mklement0
  • 382,024
  • 64
  • 607
  • 775
3

You can do this:

$str = "dog.puppy/cat.kitten/bear.cub".split('/')[2].split('.')[0]

No piping needed.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • Thanks, that works perfectly. Next I need to split the string based on capital letters, so let's say the string was `dog.puppy/cat.kitten/bearOneTwoThree.cub`, and I need the result `bear One Two Three` which is achieved using -creplace ".(?=[^a-z])", "$& ". Am I able to do this in one line? – jarrad_obrien Mar 20 '19 at 00:29
  • 1
    I suggest opening a separate question for that, and simplify the question to only use the `bearOneTwoThree` text. – Joel Coehoorn Mar 20 '19 at 00:31
  • 1
    I just figured it out. I just had to do `$str = $str.split('/')[2].split('.')[0] | ForEach-object { $_ -creplace '.(?=[^a-z])','$& ' }` – jarrad_obrien Mar 20 '19 at 00:35
1

There is an easier solution,
the .split() method uses each char to split,
using a negative index counts from back, so

> "dog.puppy/cat.kitten/bear.cub".split( '/.')[-2]
bear

Your other question can be solved with the RegEx based -csplit operator using a
nonconsuming positive lookahead and
a final -join ' ' to concatenate the array elements with a space.

$str = "dog.puppy/cat.kitten/bearOneTwoThree.cub"
$str = $str.split('/.')[-2] -csplit '(?=[A-Z])' -join ' '
$str
bear One Two Three
  • 1
    It's unfortunate, but to make your `.Split()` call work in PowerShell _Core_ as well, you must use `"dog.puppy/cat.kitten/bear.cub".split([char[]] '/.')[-2]` - see the (updated) bottom section of https://stackoverflow.com/a/41905031/45375 for an explanation (and thanks for the recent edit there). – mklement0 Mar 21 '19 at 02:15