Reputation: 4050
This is more about my weak understanding of pipelines and functions than anything, so would appreciate help. As an example:
Get-Service | Select-String win # fails
Get-Service | Out-String -Stream | Select-String win # works
I've seen explanations on why the Out-String -Stream
is required, but it frustrates me a bit, so I want a function oss
that does the Out-String -Stream
part. My idea is like this:
function oss { Out-String -Stream }
Get-Service | oss | Select-String win
But this fails. Can someone tell me how to make this work, i.e. make the oss
function pipeline compatible?
And from that, how do we then do the following (obviously this doesn't work, but I'm really curious how this would be achieved)?
function slx { Out-String -Stream | Select-String <and allow all of the same switches as Select-String> }
Get-Service | slx win
So that, effectively, slx
is a variant of Select-String
that always does the Out-String -Stream
part as well?
Upvotes: 5
Views: 1595
Reputation: 27423
This comes up a lot. Other options. In the case of get-service, both -name and -displayname take wildcards:
get-service *win*
get-service -displayname *win*
Or if wildcards aren't available, but you need to know the property name, which is usually the column heading, in this case displayname:
get-service | where displayname -match win
Sometimes something like this gets some results. It depends on what the string version of the object becomes:
get-service | where { $_ -match 'win' }
Occasionally it is useful to convert everything to a string and search it. I'm not above doing this. "/i" is case insensitive.
get-service | findstr /i win
Upvotes: 0
Reputation: 437813
Note: This answer complements marsze's helpful answer.
Windows PowerShell v5.1 and PowerShell [Core] v6+ have an oss
function built in, which effectively provides the functionality of Out-String -Stream
via a proxy function, as shown in @marsze's answer.
OutBuffer
-related code only applies to server-side proxy functions that need to guard against DoS (denial-of-service) attacks, and the throw
statements should be $PSCmdlet.ThrowTerminatingError($_)
instead - see GitHub issue #10863.While this helper function is useful, it shouldn't be required in the first place, because Select-String
should implicitly operate on formatted output to begin with:
That is, for input that isn't already a string (text), Select-String
should search the same richly formatted text representation that you see in the console (terminal).
Where-Object
.GitHub suggestion #10726 proposes just that, though there seem to be backward-compatibility concerns, even though I think there shouldn't be.
You can adapt the proxy-function approach from marsze's answers to properly implement the slx
function you envision, as advanced function Select-StringFormatted
, aliased to scs
(see rationale below; of course, you can also define slx
).
Note:
The bulk of the code is the auto-generated declaration of the same parameters that Select-String
supports; the actual proxying (wrapping) code via Out-String -Stream
is minimal.
Alias scs
is what Select-String
's built-in alias sls
should have been, given that the official alias prefix for approved verb Select
is sc
, not ls
; in PowerShell [Core] v6+ you an easily verify that with Get-Verb Select
. Since the implicit Out-String -Stream
behavior should be Select-String
's default behavior anyway, I think it makes sense to pick this alias; if/when Select-String
gets fixed, you can redefine scs
to point to Select-String
.
All parameters supported as of v7.0 are declared; if you try to use ones only introduced in 7.0 in earlier versions, namely -Culture
, -Raw
, and -NoEmphasis
, they are ignored and a warning is emitted.
Example command (compare to the output from Get-Process | sls service
):
PS> Get-Process | scs service # scs == Select-StringFormatted
395 16 3728 14656 5664 0 SecurityHealthService
360 11 3864 7696 632 0 services
173 12 3324 8628 2388 0 VGAuthService
Note: The function below is also available as an MIT-licensed Gist, and only the latter will be maintained going forward. Assuming you have looked at the linked code to ensure that it is safe (which I can personally assure you of, but you should always check), you can install it as follows:
irm https://gist.github.com/mklement0/46fea9e6e5ef1a3ceaf681c976cb68e3/raw/Select-StringFormatted.ps1 | iex
Set-Alias scs Select-StringFormatted
function Select-StringFormatted {
<#
.SYNOPSIS
Select-String wrapper command that for non-string input searches the
*formatted* object representations rather than the often useless
.ToString() representations.
All Select-String parameters are supported. See the latter's help for more.
.EXAMPLE
Get-Process | Select-StringFormatted pwsh
Output lines that contain "pwsh" in Get-Process' formatted output.
.LINK
Select-String
#>
[CmdletBinding(DefaultParameterSetName = 'File', HelpUri = 'https://go.microsoft.com/fwlink/?LinkID=2097119')]
param(
[ValidateSet('Ordinal', 'Invariant', 'Current', '', 'af', 'af-NA', 'af-ZA', 'agq', 'agq-CM', 'ak', 'ak-GH', 'am', 'am-ET', 'ar', 'ar-001', 'ar-AE', 'ar-BH', 'ar-DJ', 'ar-DZ', 'ar-EG', 'ar-EH', 'ar-ER', 'ar-IL', 'ar-IQ', 'ar-JO', 'ar-KM', 'ar-KW', 'ar-LB', 'ar-LY', 'ar-MA', 'ar-MR', 'ar-OM', 'ar-PS', 'ar-QA', 'ar-SA', 'ar-SD', 'ar-SO', 'ar-SS', 'ar-SY', 'ar-TD', 'ar-TN', 'ar-YE', 'arn', 'arn-CL', 'as', 'as-IN', 'asa', 'asa-TZ', 'ast', 'ast-ES', 'az', 'az-Cyrl', 'az-Cyrl-AZ', 'az-Latn', 'az-Latn-AZ', 'ba', 'ba-RU', 'bas', 'bas-CM', 'be', 'be-BY', 'bem', 'bem-ZM', 'bez', 'bez-TZ', 'bg', 'bg-BG', 'bm', 'bm-ML', 'bn', 'bn-BD', 'bn-IN', 'bo', 'bo-CN', 'bo-IN', 'br', 'br-FR', 'brx', 'brx-IN', 'bs', 'bs-Cyrl', 'bs-Cyrl-BA', 'bs-Latn', 'bs-Latn-BA', 'byn', 'byn-ER', 'ca', 'ca-AD', 'ca-ES', 'ca-FR', 'ca-IT', 'ccp', 'ccp-BD', 'ccp-IN', 'ce', 'ce-RU', 'ceb', 'ceb-PH', 'cgg', 'cgg-UG', 'chr', 'chr-US', 'ckb', 'ckb-IQ', 'ckb-IR', 'co', 'co-FR', 'cs', 'cs-CZ', 'cv', 'cv-RU', 'cy', 'cy-GB', 'da', 'da-DK', 'da-GL', 'dav', 'dav-KE', 'de', 'de-AT', 'de-BE', 'de-CH', 'de-DE', 'de-IT', 'de-LI', 'de-LU', 'dje', 'dje-NE', 'dsb', 'dsb-DE', 'dua', 'dua-CM', 'dv', 'dv-MV', 'dyo', 'dyo-SN', 'dz', 'dz-BT', 'ebu', 'ebu-KE', 'ee', 'ee-GH', 'ee-TG', 'el', 'el-CY', 'el-GR', 'en', 'en-001', 'en-150', 'en-AD', 'en-AE', 'en-AG', 'en-AI', 'en-AL', 'en-AR', 'en-AS', 'en-AT', 'en-AU', 'en-BA', 'en-BB', 'en-BD', 'en-BE', 'en-BG', 'en-BI', 'en-BM', 'en-BR', 'en-BS', 'en-BW', 'en-BZ', 'en-CA', 'en-CC', 'en-CH', 'en-CK', 'en-CL', 'en-CM', 'en-CN', 'en-CO', 'en-CX', 'en-CY', 'en-CZ', 'en-DE', 'en-DG', 'en-DK', 'en-DM', 'en-EE', 'en-ER', 'en-ES', 'en-FI', 'en-FJ', 'en-FK', 'en-FM', 'en-FR', 'en-GB', 'en-GD', 'en-GG', 'en-GH', 'en-GI', 'en-GM', 'en-GR', 'en-GU', 'en-GY', 'en-HK', 'en-HR', 'en-HU', 'en-ID', 'en-IE', 'en-IL', 'en-IM', 'en-IN', 'en-IO', 'en-IS', 'en-IT', 'en-JE', 'en-JM', 'en-JP', 'en-KE', 'en-KI', 'en-KN', 'en-KR', 'en-KY', 'en-LC', 'en-LR', 'en-LS', 'en-LT', 'en-LU', 'en-LV', 'en-ME', 'en-MG', 'en-MH', 'en-MM', 'en-MO', 'en-MP', 'en-MS', 'en-MT', 'en-MU', 'en-MV', 'en-MW', 'en-MX', 'en-MY', 'en-NA', 'en-NF', 'en-NG', 'en-NL', 'en-NO', 'en-NR', 'en-NU', 'en-NZ', 'en-PG', 'en-PH', 'en-PK', 'en-PL', 'en-PN', 'en-PR', 'en-PT', 'en-PW', 'en-RO', 'en-RS', 'en-RU', 'en-RW', 'en-SA', 'en-SB', 'en-SC', 'en-SD', 'en-SE', 'en-SG', 'en-SH', 'en-SI', 'en-SK', 'en-SL', 'en-SS', 'en-SX', 'en-SZ', 'en-TC', 'en-TH', 'en-TK', 'en-TO', 'en-TR', 'en-TT', 'en-TV', 'en-TW', 'en-TZ', 'en-UA', 'en-UG', 'en-UM', 'en-US', 'en-US-POSIX', 'en-VC', 'en-VG', 'en-VI', 'en-VU', 'en-WS', 'en-ZA', 'en-ZM', 'en-ZW', 'eo', 'eo-001', 'es', 'es-419', 'es-AG', 'es-AI', 'es-AR', 'es-AW', 'es-BB', 'es-BL', 'es-BM', 'es-BO', 'es-BQ', 'es-BR', 'es-BS', 'es-BZ', 'es-CA', 'es-CL', 'es-CO', 'es-CR', 'es-CU', 'es-CW', 'es-DM', 'es-DO', 'es-EA', 'es-EC', 'es-ES', 'es-FK', 'es-GD', 'es-GF', 'es-GL', 'es-GP', 'es-GQ', 'es-GT', 'es-GY', 'es-HN', 'es-HT', 'es-IC', 'es-KN', 'es-KY', 'es-LC', 'es-MF', 'es-MQ', 'es-MS', 'es-MX', 'es-NI', 'es-PA', 'es-PE', 'es-PH', 'es-PM', 'es-PR', 'es-PY', 'es-SR', 'es-SV', 'es-SX', 'es-TC', 'es-TT', 'es-US', 'es-UY', 'es-VC', 'es-VE', 'es-VG', 'es-VI', 'et', 'et-EE', 'eu', 'eu-ES', 'ewo', 'ewo-CM', 'fa', 'fa-AF', 'fa-IR', 'ff', 'ff-Latn', 'ff-Latn-BF', 'ff-Latn-CM', 'ff-Latn-GH', 'ff-Latn-GM', 'ff-Latn-GN', 'ff-Latn-GW', 'ff-Latn-LR', 'ff-Latn-MR', 'ff-Latn-NE', 'ff-Latn-NG', 'ff-Latn-SL', 'ff-Latn-SN', 'fi', 'fi-FI', 'fil', 'fil-PH', 'fo', 'fo-DK', 'fo-FO', 'fr', 'fr-BE', 'fr-BF', 'fr-BI', 'fr-BJ', 'fr-BL', 'fr-CA', 'fr-CD', 'fr-CF', 'fr-CG', 'fr-CH', 'fr-CI', 'fr-CM', 'fr-DJ', 'fr-DZ', 'fr-FR', 'fr-GA', 'fr-GF', 'fr-GN', 'fr-GP', 'fr-GQ', 'fr-HT', 'fr-KM', 'fr-LU', 'fr-MA', 'fr-MC', 'fr-MF', 'fr-MG', 'fr-ML', 'fr-MQ', 'fr-MR', 'fr-MU', 'fr-NC', 'fr-NE', 'fr-PF', 'fr-PM', 'fr-RE', 'fr-RW', 'fr-SC', 'fr-SN', 'fr-SY', 'fr-TD', 'fr-TG', 'fr-TN', 'fr-VU', 'fr-WF', 'fr-YT', 'fur', 'fur-IT', 'fy', 'fy-NL', 'ga', 'ga-IE', 'gaa', 'gaa-GH', 'gd', 'gd-GB', 'gez', 'gez-ER', 'gez-ET', 'gl', 'gl-ES', 'gn', 'gn-PY', 'gsw', 'gsw-CH', 'gsw-FR', 'gsw-LI', 'gu', 'gu-IN', 'guz', 'guz-KE', 'gv', 'gv-IM', 'ha', 'ha-GH', 'ha-NE', 'ha-NG', 'haw', 'haw-US', 'he', 'he-IL', 'hi', 'hi-IN', 'hi-Latn', 'hr', 'hr-BA', 'hr-HR', 'hsb', 'hsb-DE', 'hu', 'hu-HU', 'hy', 'hy-AM', 'ia', 'ia-001', 'id', 'id-ID', 'ig', 'ig-NG', 'ii', 'ii-CN', 'io', 'io-001', 'is', 'is-IS', 'it', 'it-CH', 'it-IT', 'it-SM', 'it-VA', 'iu', 'iu-CA', 'ja', 'ja-JP', 'jbo', 'jbo-001', 'jgo', 'jgo-CM', 'jmc', 'jmc-TZ', 'jv', 'jv-ID', 'ka', 'ka-GE', 'kab', 'kab-DZ', 'kaj', 'kaj-NG', 'kam', 'kam-KE', 'kcg', 'kcg-NG', 'kde', 'kde-TZ', 'kea', 'kea-CV', 'khq', 'khq-ML', 'ki', 'ki-KE', 'kk', 'kk-KZ', 'kkj', 'kkj-CM', 'kl', 'kl-GL', 'kln', 'kln-KE', 'km', 'km-KH', 'kn', 'kn-IN', 'ko', 'ko-KP', 'ko-KR', 'kok', 'kok-IN', 'kpe', 'kpe-GN', 'kpe-LR', 'ks', 'ks-Arab', 'ks-Arab-IN', 'ks-Aran', 'ks-Aran-IN', 'ks-Deva', 'ks-IN', 'ksb', 'ksb-TZ', 'ksf', 'ksf-CM', 'ksh', 'ksh-DE', 'ku', 'ku-TR', 'kw', 'kw-GB', 'ky', 'ky-KG', 'lag', 'lag-TZ', 'lb', 'lb-LU', 'lg', 'lg-UG', 'lkt', 'lkt-US', 'ln', 'ln-AO', 'ln-CD', 'ln-CF', 'ln-CG', 'lo', 'lo-LA', 'lrc', 'lrc-IQ', 'lrc-IR', 'lt', 'lt-LT', 'lu', 'lu-CD', 'luo', 'luo-KE', 'luy', 'luy-KE', 'lv', 'lv-LV', 'mas', 'mas-KE', 'mas-TZ', 'mer', 'mer-KE', 'mfe', 'mfe-MU', 'mg', 'mg-MG', 'mgh', 'mgh-MZ', 'mgo', 'mgo-CM', 'mi', 'mi-NZ', 'mk', 'mk-MK', 'ml', 'ml-IN', 'mn', 'mn-MN', 'mni', 'mni-Beng', 'mni-Beng-IN', 'mni-Mtei', 'mni-Mtei-IN', 'moh', 'moh-CA', 'mr', 'mr-IN', 'ms', 'ms-Arab', 'ms-Arab-BN', 'ms-Arab-MY', 'ms-BN', 'ms-MY', 'ms-SG', 'mt', 'mt-MT', 'mua', 'mua-CM', 'my', 'my-MM', 'myv', 'myv-RU', 'mzn', 'mzn-IR', 'naq', 'naq-NA', 'nb', 'nb-NO', 'nb-SJ', 'nd', 'nd-ZW', 'nds', 'nds-DE', 'nds-NL', 'ne', 'ne-IN', 'ne-NP', 'nl', 'nl-AW', 'nl-BE', 'nl-BQ', 'nl-CW', 'nl-NL', 'nl-SR', 'nl-SX', 'nmg', 'nmg-CM', 'nn', 'nn-NO', 'nnh', 'nnh-CM', 'nqo', 'nqo-GN', 'nr', 'nr-ZA', 'nso', 'nso-ZA', 'nus', 'nus-SS', 'ny', 'ny-MW', 'nyn', 'nyn-UG', 'oc', 'oc-FR', 'om', 'om-ET', 'om-KE', 'or', 'or-IN', 'os', 'os-GE', 'os-RU', 'pa', 'pa-Arab', 'pa-Arab-PK', 'pa-Aran', 'pa-Aran-PK', 'pa-Guru', 'pa-Guru-IN', 'pl', 'pl-PL', 'ps', 'ps-AF', 'ps-PK', 'pt', 'pt-AO', 'pt-BR', 'pt-CH', 'pt-CV', 'pt-FR', 'pt-GQ', 'pt-GW', 'pt-LU', 'pt-MO', 'pt-MZ', 'pt-PT', 'pt-ST', 'pt-TL', 'qu', 'qu-BO', 'qu-EC', 'qu-PE', 'rm', 'rm-CH', 'rn', 'rn-BI', 'ro', 'ro-MD', 'ro-RO', 'rof', 'rof-TZ', 'ru', 'ru-BY', 'ru-KG', 'ru-KZ', 'ru-MD', 'ru-RU', 'ru-UA', 'rw', 'rw-RW', 'rwk', 'rwk-TZ', 'sa', 'sa-IN', 'sah', 'sah-RU', 'saq', 'saq-KE', 'sat', 'sat-Deva', 'sat-Deva-IN', 'sat-Olck', 'sat-Olck-IN', 'sbp', 'sbp-TZ', 'sc', 'sc-IT', 'scn', 'scn-IT', 'sd', 'sd-Deva', 'sd-PK', 'se', 'se-FI', 'se-NO', 'se-SE', 'seh', 'seh-MZ', 'ses', 'ses-ML', 'sg', 'sg-CF', 'shi', 'shi-Latn', 'shi-Latn-MA', 'shi-Tfng', 'shi-Tfng-MA', 'si', 'si-LK', 'sk', 'sk-SK', 'sl', 'sl-SI', 'smn', 'smn-FI', 'sn', 'sn-ZW', 'so', 'so-DJ', 'so-ET', 'so-KE', 'so-SO', 'sq', 'sq-AL', 'sq-MK', 'sq-XK', 'sr', 'sr-Cyrl', 'sr-Cyrl-BA', 'sr-Cyrl-ME', 'sr-Cyrl-RS', 'sr-Cyrl-XK', 'sr-Latn', 'sr-Latn-BA', 'sr-Latn-ME', 'sr-Latn-RS', 'sr-Latn-XK', 'ss', 'ss-SZ', 'ss-ZA', 'st', 'st-LS', 'st-ZA', 'sv', 'sv-AX', 'sv-FI', 'sv-SE', 'sw', 'sw-CD', 'sw-KE', 'sw-TZ', 'sw-UG', 'syr', 'syr-IQ', 'syr-SY', 'ta', 'ta-IN', 'ta-LK', 'ta-MY', 'ta-SG', 'te', 'te-IN', 'teo', 'teo-KE', 'teo-UG', 'tg', 'tg-TJ', 'th', 'th-TH', 'ti', 'ti-ER', 'ti-ET', 'tig', 'tig-ER', 'tk', 'tk-TM', 'tn', 'tn-BW', 'tn-ZA', 'to', 'to-TO', 'tr', 'tr-CY', 'tr-TR', 'trv', 'trv-TW', 'ts', 'ts-ZA', 'tt', 'tt-RU', 'twq', 'twq-NE', 'tzm', 'tzm-MA', 'ug', 'ug-CN', 'uk', 'uk-UA', 'ur', 'ur-Arab', 'ur-Arab-IN', 'ur-Arab-PK', 'ur-Aran', 'ur-Aran-IN', 'ur-Aran-PK', 'ur-IN', 'ur-PK', 'uz', 'uz-Arab', 'uz-Arab-AF', 'uz-Cyrl', 'uz-Cyrl-UZ', 'uz-Latn', 'uz-Latn-UZ', 'vai', 'vai-Latn', 'vai-Latn-LR', 'vai-Vaii', 'vai-Vaii-LR', 've', 've-ZA', 'vi', 'vi-VN', 'vun', 'vun-TZ', 'wa', 'wa-BE', 'wae', 'wae-CH', 'wal', 'wal-ET', 'wo', 'wo-SN', 'wuu', 'xh', 'xh-ZA', 'xog', 'xog-UG', 'yav', 'yav-CM', 'yi', 'yi-001', 'yo', 'yo-BJ', 'yo-NG', 'yue', 'yue-Hans', 'yue-Hans-CN', 'yue-Hant', 'yue-Hant-HK', 'zgh', 'zgh-MA', 'zh', 'zh-Hans', 'zh-Hans-CN', 'zh-Hans-HK', 'zh-Hans-MO', 'zh-Hans-SG', 'zh-Hant', 'zh-Hant-CN', 'zh-Hant-HK', 'zh-Hant-MO', 'zh-Hant-TW', 'zu', 'zu-ZA')]
[ValidateNotNull()]
[string]
${Culture},
[Parameter(ParameterSetName = 'ObjectRaw', Mandatory, ValueFromPipeline)]
[Parameter(ParameterSetName = 'Object', Mandatory, ValueFromPipeline)]
[AllowEmptyString()]
[AllowNull()]
[psobject]
${InputObject},
[Parameter(Mandatory, Position = 0)]
[string[]]
${Pattern},
[Parameter(ParameterSetName = 'FileRaw', Mandatory, Position = 1, ValueFromPipelineByPropertyName)]
[Parameter(ParameterSetName = 'File', Mandatory, Position = 1, ValueFromPipelineByPropertyName)]
[string[]]
${Path},
[Parameter(ParameterSetName = 'LiteralFileRaw', Mandatory, ValueFromPipelineByPropertyName)]
[Parameter(ParameterSetName = 'LiteralFile', Mandatory, ValueFromPipelineByPropertyName)]
[Alias('PSPath', 'LP')]
[string[]]
${LiteralPath},
[Parameter(ParameterSetName = 'LiteralFileRaw', Mandatory)]
[Parameter(ParameterSetName = 'FileRaw', Mandatory)]
[Parameter(ParameterSetName = 'ObjectRaw', Mandatory)]
[switch]
${Raw},
[switch]
${SimpleMatch},
[switch]
${CaseSensitive},
[Parameter(ParameterSetName = 'File')]
[Parameter(ParameterSetName = 'Object')]
[Parameter(ParameterSetName = 'LiteralFile')]
[switch]
${Quiet},
[switch]
${List},
[switch]
${NoEmphasis},
[ValidateNotNullOrEmpty()]
[string[]]
${Include},
[ValidateNotNullOrEmpty()]
[string[]]
${Exclude},
[switch]
${NotMatch},
[switch]
${AllMatches},
[ValidateNotNullOrEmpty()]
[System.Text.Encoding]
${Encoding},
[ValidateRange(0, 2147483647)]
[ValidateNotNullOrEmpty()]
[ValidateCount(1, 2)]
[int[]]
${Context})
begin {
# Ignore parameters that are only supported in v7+
if ($PSVersionTable.PSVersion.Major -lt 7) {
('Culture', 'Raw', 'NoEmphasis').ForEach( {
if ($PSBoundParameters.ContainsKey($_)) {
$null = $PSBoundParameters.Remove($_)
Write-Warning "Ignoring parameter $_, because it is only supported in PowerShell 7.0 or above."
}
})
}
# Set up the steppable pipeline.
try {
$commandToWrap = { Out-String -Stream | Select-String @PSBoundParameters }
$steppablePipeline = $commandToWrap.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
}
}
process {
$steppablePipeline.Process($_)
}
end {
$steppablePipeline.End()
}
}
Upvotes: 2
Reputation: 17055
I appreciate that you want to learn, so I will not just give you a working solution, but also explain a few things.
It seems like your you have a basic understanding of how functions and the pipeline work. If you want to learn more about it have a look at about_Pipelines and about_Functions (and related help topics), or let me know what in particular you want to know in the comments.
One thing you need to understand though if you are new to Powershell, is that it it's object-based, not text-based. It's objects that are passed through the pipeline, and you should work with them as such, and not convert them to text (unless for file output).
The documentation about Select-String states specifically, that it is meant for finding text in strings and files. But this is not what you are trying to do here. As I said, work with objects, not text. So omit the Out-String
. The proper command to filter objects in a pipeline is Where-Object or one of its aliases where
or ?
.
Example:
Get-Service | where { $_.DisplayName -like *win* }
# in newer Powershell versions, this shorter syntax is also possible:
Get-Service | where DisplayName -like *win*
But as you specifically asked about making a function "pipeline aware", this can be done in several ways. But your problem is not only getting input from the pipeline, but also they way you pass it to Out-String
, because the output will change if you pass only one item at a time.
Basically you have to pass the input all at once. So, the simplest way to achieve this in your case is using the $Input automatic variable:
function oss { $Input | Out-String -Stream }
If you need to process one pipeline item at a time, there are more ways to get pipeline input. For example an advanced function:
function example {
process {
$_ # current item
}
}
You can also use the parameter attribute to bind the pipeline input to a function parameter:
function example {
param(
[Parameter(ValueFromPipeline = $true)]
$InputObject
)
process {
$InputObject # current item
}
}
The last way I can think of is using a filter, which is a special kind of function which is specifically designed to perform an operation on each pipeline element:
filter example {
$_ # current item
}
To learn more about it I recommend a google search which will quickly provide you with useful articles such as Build a PowerShell function for the pipeline or Incorporating Pipelined Input into PowerShell Functions.
If you truly want to build a method that is basically a wrapper for another cmdlet like Out-String
or Select-String
with all the same switches, you should have a look at proxy functions. But I think this would be overkill for what you're trying to do:
function oss {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline = $true)]
[psobject]$InputObject
)
begin
{
try {
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
# set -Stream switch always
$PSBoundParameters["Stream"] = $true
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Out-String', [System.Management.Automation.CommandTypes]::Cmdlet)
$scriptCmd = {& $wrappedCmd @PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
} catch {
throw
}
}
process
{
try {
$steppablePipeline.Process($_)
} catch {
throw
}
}
end
{
try {
$steppablePipeline.End()
} catch {
throw
}
}
}
Upvotes: 6