次の方法で共有


文字列内の変数置換について知りたいこと

文字列で変数を使用する方法は多数あります。 私はこの変数の置換を呼び出していますが、変数の値を含むように文字列を書式設定したい場合はいつでも参照しています。 これは、私がよく新しいスクリプターに説明することに気が付くものです。

この記事の 元のバージョン は、@KevinMarquetteによって書かれたブログに登場しました. PowerShell チームは、このコンテンツを Microsoft と共有してくれた Kevin に感謝します。 PowerShellExplained.comで彼のブログをチェックしてください.

連結

メソッドの最初のクラスは連結と呼ばれます。 基本的には、いくつかの文字列を取得し、それらを結合します。 連結を使用して書式設定された文字列を構築する長い歴史があります。

$name = 'Kevin Marquette'
$message = 'Hello, ' + $name

追加する値が少ない場合は、連結で問題なく動作します。 しかし、これはすぐに複雑になる可能性があります。

$first = 'Kevin'
$last = 'Marquette'
$message = 'Hello, ' + $first + ' ' + $last + '.'

この簡単な例は、既に読みにくくなっています。

変数の置換

PowerShell には、より簡単な別のオプションがあります。 変数は、文字列内で直接指定できます。

$message = "Hello, $first $last."

文字列の周囲で使用する引用符の種類によって、違いが生じます。 二重引用符で囲まれた文字列では置換が可能ですが、単一引用符で囲まれた文字列では置換できません。 一方または他方が必要な場合があるので、オプションがあります。

コマンドの置換

プロパティの値を文字列に取り込もうとすると、少し複雑になります。 ここで、多くの新しい人がつながれるのです。 まず、彼らが何をすべきだと思うかを示しましょう(そして額面では、ほとんどそう見えます)。

$directory = Get-Item 'C:\windows'
$message = "Time: $directory.CreationTime"

$directoryからCreationTimeを取得する必要がありますが、代わりにこのTime: C:\windows.CreationTimeを値として取得します。 その理由は、この型の置換では基本変数のみが表示されるためです。 期間は文字列の一部と見なされるため、値の解決が停止します。

これは、このオブジェクトが文字列に配置されたときに既定値として文字列を与えるという点で起こります。 一部のオブジェクトでは、 System.Collections.Hashtableのように型名が付けられます。 ただ、注意が必要な何か。

PowerShell を使用すると、特別な構文を使用して文字列内でコマンドを実行できます。 これにより、これらのオブジェクトのプロパティを取得し、他のコマンドを実行して値を取得できます。

$message = "Time: $($directory.CreationTime)"

これはいくつかの状況でうまく機能しますが、変数が少ししかない場合は連結と同じようにクレイジーになる可能性があります。

コマンドの実行

文字列内でコマンドを実行できます。 私はこのオプションを持っているにもかかわらず、私はそれが好きではありません。 すぐに煩雑になり、デバッグが困難になります。 コマンドを実行して変数に保存するか、書式指定文字列を使用します。

$message = "Date: $(Get-Date)"

文字列の書式設定

.NET には、操作が非常に簡単な文字列を書式設定する方法があります。 最初に、同じことを行う PowerShell ショートカットを表示する前に、静的メソッドを示します。

# .NET string format string
[string]::Format('Hello, {0} {1}.',$first,$last)

# PowerShell format string
'Hello, {0} {1}.' -f $first, $last

ここで起こっているのは、トークン {0} および {1}の文字列が解析され、その数値を使用して指定された値から選択されるということです。 文字列内の 1 つの値を繰り返す場合は、その値番号を再利用できます。

文字列が複雑になるほど、このアプローチから得られる値が多くなります。

値を配列として書式設定する

書式行が長すぎる場合は、最初に値を配列に配置できます。

$values = @(
    "Kevin"
    "Marquette"
)
'Hello, {0} {1}.' -f $values

配列全体を渡しているので、これはスプラッティングではありませんが、考え方は似ています。

高度な書式設定

多くの書式設定オプションが既に十分に 文書化 されているため、私は意図的にこれらを.NETから来ていると呼んだ。 さまざまなデータ型を書式設定する組み込みの方法があります。

"{0:yyyyMMdd}" -f (Get-Date)
"Population {0:N0}" -f  8175133
20211110
Population 8,175,133

私はそれらに行くつもりはありませんが、私はちょうどあなたがそれを必要とする場合、これは非常に強力なフォーマットエンジンであることを知らせたかった。

文字列の結合

実際に値のリストを連結したい場合があります。 これを行うことができる -join 演算子があります。 文字列間で結合する文字を指定することもできます。

$servers = @(
    'server1'
    'server2'
    'server3'
)

$servers  -join ','

区切り記号なしで文字列を -join する場合は、空の文字列 ''を指定する必要があります。 しかし、それが必要な場合は、より高速なオプションがあります。

[string]::Concat('server1','server2','server3')
[string]::Concat($servers)

また、文字列も -split できることを指摘する価値があります。

Join-Path

多くの場合、これは見落とされますが、ファイル パスを構築するための優れたコマンドレットです。

$folder = 'Temp'
Join-Path -Path 'C:\windows' -ChildPath $folder

これに関する素晴らしい点は、値をまとめると円記号が正しく動作することです。 これは、ユーザーまたは構成ファイルから値を取得する場合に特に重要です。

これは、 Split-PathTest-Pathにも合います。 私はまた、 ファイルの読み取りと保存に関する私の投稿でこれらをカバーしています。

文字列は配列です

先に進む前に、ここで文字列を追加することを言及する必要があります。 文字列は単なる文字の配列であることを覚えておいてください。 複数の文字列を一緒に追加すると、毎回新しい配列が作成されます。

この例を見てください。

$message = "Numbers: "
foreach($number in 1..10000)
{
    $message += " $number"
}

それは非常に基本的に見えますが、見えないのは、文字列が $message に追加されるたびに、まったく新しい文字列が作成されるということです。 メモリが割り当てられ、データがコピーされ、古いものが破棄されます。 数回しか実行されない場合は大した問題ではありませんが、このようなループは本当に問題を明らかにします。

StringBuilder

StringBuilder は、多数の小さな文字列から大きな文字列を構築する場合にも非常に人気があります。 理由は、追加したすべての文字列を収集するだけで、値を取得するときに最後にすべての文字列を連結するためです。

$stringBuilder = New-Object -TypeName "System.Text.StringBuilder"

[void]$stringBuilder.Append("Numbers: ")
foreach($number in 1..10000)
{
    [void]$stringBuilder.Append(" $number")
}
$message = $stringBuilder.ToString()

繰り返しますが、これは私が.NETに手を差し伸べているものです。 私はもうそれを頻繁に使用しませんが、それがそこにあることを知って良いことです。

中かっこを使用した線付け

これは、文字列内のサフィックス連結に使用されます。 変数にクリーンな単語境界がない場合があります。

$test = "Bet"
$tester = "Better"
Write-Host "$test $tester ${test}ter"

Redditor u/real_parbold に感謝します。

この方法の代替手段を次に示します。

Write-Host "$test $tester $($test)ter"
Write-Host "{0} {1} {0}ter" -f $test, $tester

私は個人的にこのためにフォーマット文字列を使用しますが、これはあなたが野生でそれを見る場合に知って良いです。

トークンの検索と置換

これらの機能のほとんどは、独自のソリューションをロールする必要性を制限しますが、内部の文字列を置き換える大きなテンプレート ファイルがある場合があります。

テキストが多いファイルからテンプレートを取り込んだとします。

$letter = Get-Content -Path TemplateLetter.txt -RAW
$letter = $letter -replace '#FULL_NAME#', 'Kevin Marquette'

置換するトークンが多数ある場合があります。 そのトリックは、見つけて置き換えるのが簡単な非常に明確なトークンを使用することです。 私はそれを区別するのに役立つ両端に特殊文字を使用する傾向があります。

私は最近、これにアプローチする新しい方法を見つけました。 これは一般的に使用されるパターンであるため、このセクションはここに残すことにしました。

複数のトークンを置き換える

置き換える必要があるトークンの一覧がある場合は、より汎用的なアプローチを取ります。 それらをハッシュテーブルに配置し、それらを反復処理して置換を行います。

$tokenList = @{
    Full_Name = 'Kevin Marquette'
    Location = 'Orange County'
    State = 'CA'
}

$letter = Get-Content -Path TemplateLetter.txt -RAW
foreach( $token in $tokenList.GetEnumerator() )
{
    $pattern = '#{0}#' -f $token.key
    $letter = $letter -replace $pattern, $token.Value
}

必要に応じて、これらのトークンを JSON または CSV から読み込むことができます。

ExecutionContext ExpandString

単一引用符で置換文字列を定義し、後で変数を展開する巧妙な方法があります。 この例を見てください。

$message = 'Hello, $Name!'
$name = 'Kevin Marquette'
$string = $ExecutionContext.InvokeCommand.ExpandString($message)

現在の実行コンテキストで InvokeCommand.ExpandString を呼び出すと、現在のスコープ内の変数が置換に使用されます。 ここで重要なのは、変数が存在する前に $message を非常に早く定義できることです。

少しだけ拡張すれば、この置換をさまざまな値で繰り返し実行できます。

$message = 'Hello, $Name!'
$nameList = 'Mark Kraus','Kevin Marquette','Lee Dailey'
foreach($name in $nameList){
    $ExecutionContext.InvokeCommand.ExpandString($message)
}

この考え方を続けるために。これを行うには、テキスト ファイルから大きな電子メール テンプレートをインポートできます。 私はこの提案のために マーククラウス に感謝する必要があります.

お客様に最適なもの

私はフォーマット文字列アプローチのファンです。 私は間違いなく、より複雑な文字列で、または複数の変数がある場合にこれを行います。 非常に短いものでは、私はこれらのいずれかを使用することができます。

ほかに何か。

私はこの1で多くの地面をカバーしました。 私の願いは、あなたが何か新しいことを学ぶことです。

文字列補間を可能にするメソッドと機能の詳細については、参照ドキュメントの次の一覧を参照してください。