Полезный ответ Lee_Daily хорошо объясняет проблему с вашей попыткой и предлагает рабочее решение, в том числе с использованием унарной формы -join
.
Просто дляобъясните будущим читателям , почему с использованием только .Substring()
означает , а не , а параметр для реализации extract- не более -N-символов.логика: попытка извлечь подстроку, превышающую длину входной строки, вызывает исключение :
PS> 'abc'.Substring(0, 2) # OK
ab
PS> 'abc'.Substring(0, 4) # !! Exception, because at most 3 chars. can be extracted
Exception [...]: Index and length must refer to a location within the string. [...]
Ваш подход к решению этой проблемы с использованием трубопровода with Select-Object
немного сложновато , хотя.
Ниже приведены более эффективные альтернативы, использующие выражения .
Полезный ответ Эсперенто предлагает самое эффективное решение, использующее только функции .NET , хотя это немного "шумно", поскольку требует вызова вложенного метода ипеременная (а не литерал) в качестве входных данных, потому что на эту переменную нужно ссылаться при вызове вложенного метода.
Подходы ниже более PowerShell-идиоматические.
LotPings , вcomment, предлагает краткое решение, основанное на том факте, что строки могут неявно рассматриваться как символьные массивы , поэтому можно применять разбиение массива ;обратите внимание, что индексы основаны на 0
:
PS> -join 'abc'[0..1]
ab
PS> -join 'abc'[0..3] # equivalent of: 'abc'.Substring(0, 4), but by default without error
abc
Выражение диапазона 0..3
соответствует массиву 0, 1, 2, 3
, в результате чего символы с указанными индексами возвращаются как символмассив, который -join
затем собирается в строку.
По умолчанию PowerShell игнорирует индексы за пределами массивов, но предостережение это , еслиSet-StrictMode -Version 3
или выше, выше также вызовет ошибку .
A более эффективная альтернатива, которая не чувствительна к Set-StrictMode
, используйте оператор -replace
с регулярным выражением (регулярное выражение).
Тем не менее, это решение несколько неясное .
PS> 'abc' -replace '(?<=.{2}).+' # equivalent of 'abc'.Substring(0, 2)
ab
PS> 'abc' -replace '(?<=.{4}).+' # always-safe equivalent of 'abc'.Substring(0, 4)
abc
.{4}
точно соответствует 4
символам (.
) (неявно) в начале строки, без включения этих символов в совпадение ((?<=...)
, утверждение с предварительным просмотром));.+
затем сопоставляет все оставшиеся символы (один или несколько).
Чистый эффект состоит в том, что все входные строки с более 4 символов имеют все из 5-го символа заменяется пустой строкой (из-за отсутствия замещающего операнда), оставляя фактически только первые 4 символа.
Входные строки с 4 или менее символов пропускаются как есть (извлечение не требуется).
Для мультистрока входные строки , требуется немного больше работы (встроенный параметр (?s)
, чтобы .
соответствовал символам новой строки (`n
) тоже):
PS> "a`nbc" -replace '(?s)(?<=.{3}).+' # extract first 3 chars as string: "a`n"
a
b
Такжерассмотрите возможность использования простой вспомогательной функции :
# Safe equivalent of:
# $String.Substring($Start)
# $String.Substring($Start, $Length)
function substr ([string] $String, [int] $Start = 0, [int] $Length = -1) {
if ($Length -eq -1 -or $Start + $Length -ge $String.Length) {
$String.Substring($Start)
}
else {
$String.Substring($Start, $Length)
}
}
Обратите внимание, что вам нужно вызывать ее с синтаксисом аргументов (подобными оболочке, аргументы, разделенные пробелами), как с любой функцией в PowerShell:
PS> substr 'abc' -Length 2 # same as: substr abc 0 2 / substr -String abc -Start 0 -Length 2
ab
PS> substr 'abc' -Length 4
abc
Наконец, обратите внимание, что есть RFC для введения строковых манипуляцийв командлетах (еще не реализовано в PowerShell Core 6.2.0-preview.1), в котором, среди прочего, предлагается командлет Get-Substring
для эффективной манипуляции с подстрокой в конвейере.