ファイルの連結について、いい記事があります。
私が書くコードでは Set-Content/Add-Content を使わないです。が、なぜなのかを振り返るのもいいでしょう。
今回は、 Set-Content/Add-Content と Out-File/Out-File -Append の違いについてです。
- tl;dr;
- 上書きと追記
- 書き込み方法の違い と NoClobber による上書き防止
- Write/Read ロック
- デフォルトEncoding の違い
- InputObjectが空の場合のファイル作成挙動
- PassThru の可否
- Credential の可否
- Include/Exclude の可否
- Filter の可否
- Transaction の可否
tl;dr;
記事で紹介したコードは GitHubにおいておきます。
機能のまとめです。 私がSet-Content/Add-Content を使うことは、おそらく今後もないでしょう。
機能 | Content | Out-File |
---|---|---|
上書き | Set-Content | Out-File |
追記 | Add-Content | Out-File -Append |
NoClobberスイッチ | X | O |
Write Lock | O | O |
Read Lock | O | X |
PowerShell 5.1 デフォルトEncoding | ASCII | UCS-2 Little Endian |
PowerShell 6以降 デフォルトEncoding | utf-8 | utf-8 |
Encoding指定 | O | O |
InputObjectが空の場合のファイル作成 | X | O |
PassThruスイッチ | O | X |
Credentialスイッチ | △ | X |
Includeスイッチ | O | X |
Excludeスイッチ | O | X |
Filterスイッチ | O | X |
Transactionスイッチ | △ | X |
- △ = ファイルシステムプロバイダでは使えないので無意味
一見すると、Set-Content
/Add-Content
が良さそうですが書き込み中にRead Lock かかるのは困ります。また、-PassThru
が欲しくなる機会は少なく、ップすればできるので問題ではありません。
上書きと追記
対応する機能は次の通りです。(リンクは 7.2 を示します)
Cmdlet | 上書き | 追記 |
---|---|---|
Content | Set-Content | Add-Content |
Out-File | Out-File | Out-File -Append |
両方とも -Encoding パラメータで、エンコードもセットできます。
どのような違いがあるのでしょうか。
書き込み方法の違い と NoClobber による上書き防止
挙動を確認していきましょう。
上書き
Out-File
はパスのみ指定で上書き保存されますSet-Content
は上書き保存用の Cmdletです
両方、書き込み先にファイルがなかった場合はファイルを作成します。また、書き込み先にすでにファイルがあった場合は上書きします。
#region 1. 書き込み方法の違い と NoClobber による上書き防止 # 上書き保存 (ファイルがない場合は作成) 1..10 | Out-File out.log -Encoding UTF8 1..10 | Set-Content content.log -Encoding UTF8
上書き防止
Out-File -NoClobber
スイッチで、ファイルがすでに存在した場合は上書きしません。つまり上書き保存防止です。実際、Alias にNoOverwrite
とついていますSet-Content
やAdd-Content
に-NoClobber
スイッチはないため、上書き防止はできません
# 上書き保存禁止 (ファイルがない場合は作成) 1..10 | Out-File outNoClobber.log -Encoding UTF8 -NoClobber # Set-Content/Add-Content には -NoClobber がなく、上書き禁止を制限できない
Out-File : The file 'D:\content\outNoClobber.log' already exists. At line:1 char:9 + 1..10 | Out-File outNoClobber.log -Encoding UTF8 -NoClobber + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ResourceExists: (D:\content\outNoClobber.log:String) [Out-File], IOException + FullyQualifiedErrorId : NoClobber,Microsoft.PowerShell.Commands.OutFileCommand
Out-File -NoClobber -Force の併用
-Force
を使うと、Out-File -Force
、Set-Content -Force
、Add-Content -Force
いずれも読み込み専用ファイルに書き込みできます- PowerShell 5.1 では、
Out-File -NoClobber -Force
とスイッチを併用すると-Force
のみが有効になります - PowerShell 7.2.5 では、
Out-File -NoClobber -Force
とスイッチを併用すると-NoClobber
のみが有効になります。上書き保存は拒否されます
# PowerShell 5.1 では上書き保存 (ファイルがない場合は作成/NoClobberとForceを併用するとForce(読み込み専用ファイルへの書き込み)優先) 1..10 | Out-File outNoClobberForce.log -Encoding UTF8 -NoClobber -Force 1..10 | Set-Content contentForce.log -Encoding UTF8 -Force 1..10 | Add-Content contentAddForce.log -Encoding UTF8 -Force # Set-Content には -NoClobber がなく、上書き禁止を制御できない。つまり Forceとなる。
# PowerShell 7.2.5 では読み込み専用ファイルへの書き込みとみなされて上書きできません。 $ 1..10 | Out-File outNoClobberForce.log -Encoding UTF8 -NoClobber -Force Out-File: The file 'D:\outNoClobberForce.log' already exists.
追記
Out-File -Append
で追記しますAdd-Content
は、追記用のCmdletです
両方、書き込み先にファイルがなかった場合は、ファイルを作成します。また、書き込み先にすでにファイルがあった場合は、上書きします。
# 追記 (ファイルがない場合は作成) 1..10 | Out-File outAppend.log -Encoding UTF8 -Append 1..10 | Add-Content contentAdd.log -Encoding UTF8
Out-File -NoClobber -Append の併用
Out-File -NoClobber -Append
とスイッチを併用すると、-Append
のみが有効になります。(PowerShell 5.1 も PowerShell 7.2.5 も変わらず)
# 追記 (ファイルがない場合は作成/NoClobberとAppendを併用するとAppend優先) 1..10 | Out-File outAppendNoClobber.log -Append -NoClobber # Add-Content には -NoClobberがないため、上書き禁止を制御できない。
Write/Read ロック
Out-File
は、実行中は Write Lock がかかりますSet-Content
やAdd-Content
は、実行中は Write Lock / Read Lock がかかります
私が Out-File しか使わないのはログ出力として利用しているからです。ログは 実行中の内容を読めないと困るので、Out-File の読み込みロックがかからない性質はもってこいです。
書き込みを一時停止させつつ、その間に作成しているログを開いて挙動を確認してみましょう。
Out-File
# Out-File は書き込み中は Read/Write Locking のうち Write Lockingのみ # つまり書き込み中に、その内容を読み取れる 1..10 |%{$_;sleep -Milliseconds 500} | Out-File out.log -Encoding UTF8
- 複数のホストから同時にファイルへ書きこもうとしても、Write ロックがかかっています
Out-File : The process cannot access the file 'D:\content\out.log' because it is being used by another process. At line:1 char:40 + 1..10 |%{$_;sleep -Milliseconds 500} | Out-File out.log -Encoding UTF8 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : OpenError: (:) [Out-File], IOException + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.OutFileCommand
- しかしReadロックがかかっていないので読み込み専用で開けます
- 書き込み途中の内容が見れました
- また、書き込みにしたがって、ファイルサイズが大きくなっていっている様子も Explorerから見れます
Set-Content / Add-Content
# Set-Content/Add-Content は書き込み中は Read/Write Locking のうち Read/Write Locking両方がかかる # つまり書き込み中、その内容は読み取れない 1..10 |%{$_;sleep -Milliseconds 500} | Set-Content content.log -Encoding UTF8
- 複数のホストから同時にファイルへ書きこもうとしても、Write ロックがかかっていますね
Add-Content : The process cannot access the file 'D:\content\content.log' because it is being used by another process. At line:1 char:33 + 1..10 |%{$_;sleep -Seconds 4} | Add-Content content.log + ~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (D:\content\content.log:String) [Add-Content], IOException + FullyQualifiedErrorId : GetContentWriterIOError,Microsoft.PowerShell.Commands.AddContentCommand
- Read ロックがかかっているので、読み込みすらできません
- 書き込みにしたがってファイルサイズが大きくなっていっている様子は Explorerから見えず 0KBのまま、完了後にファイルサイズが更新されます
デフォルトEncoding の違い
-Encoding
を指定しなかった場合の、Encodingにも違いがあります。
- PowerShell 5.1 では、Out-file と Set-Content/Add-Content を Encoding 指定せずに混ぜると、ファイルエンコーディングが壊れます。(混ぜるな危険)
- PowerShell 6 以降は、デフォルトエンコーディングが utf-8 に統一されたので問題が起こりません。(PowerShell 5.1 で作ったファイルに PowerShell 6 以降で読み書き時は注意がいります)
# Out-File のデフォルトエンコーディングは UCS-2 Little Endian 1..10 | Out-File outEncoding.log # Set-Content/Add-Content のデフォルトエンコーディングは ASCII 1..10 | Set-Content contentEncoding.log # ハッシュ値で確認 Get-FileHash outEncoding.log,contentEncoding.log
- ハッシュ値を比べてみてもわかりますね
Algorithm Hash Path --------- ---- ---- SHA256 BD45EFBC002BB183C495E8AFFFF54DEDA45915C9AFC2F0CA0CDF1130D0466B8A D:\content\outEncoding.log SHA256 C6011831661EAA30565BF87A2793DE08BEC53FF0E8F29C4404869C049925066B D:\content\contentEncoding.log
もちろんEncodingを指定すれば問題ありません。
# Out-File も Set-Content/Add-Content もエンコーディングを指定すれば一緒 1..10 | Out-File outEncoding.log -Encoding utf8 1..10 | Set-Content contentEncoding.log -Encoding UTF8 # ハッシュ値で確認するとデフォルトエンコーディングが異なることがわかる。 Get-FileHash outEncoding.log,contentEncoding.log
一緒ですね。
Algorithm Hash Path --------- ---- ---- SHA256 F7F5796614E196FF5893D06D826BCA4DC7C40A6D5403C624B706B9C8A029F17A D:\content\outEncoding.log SHA256 F7F5796614E196FF5893D06D826BCA4DC7C40A6D5403C624B706B9C8A029F17A D:\content\contentEncoding.log
InputObjectが空の場合のファイル作成挙動
Out-File
はパイプライン上流から渡されたコマンド実行結果が空でも、ファイル作成/書き込みを行いますSet-Content
やAdd-Content
は、パイプライン上流から渡されたコマンド実行結果が空の場合、ファイル作成/書き込みを行いません
# Out-Fileは、結果がNullでもまずファイルを作成する Get-ChildItem d:\empty | Out-File outNull.log # フォルダが空でも作る # Set-Content/Add-Contentは、結果が空だとファイルを作成しない(エラーにもならない) Get-ChildItem d:\empty | Set-Content contentNull.log # フォルダが空だと作らない $null | Set-Content contentNull.log # これだとつくっちゃうけどね
Out-File はファイルを作成していますが、Set-Content は作っていないのがわかります。
PassThru の可否
Out-File
は-PassThru
スイッチがないため、パイプラインから渡された結果を標準出力に返せませんSet-Content -PassThru
やAdd-Content -PassThru
で、パイプラインから渡された結果を標準出力に返せます
# Out-File は、-PassThru スイッチを持たず、書き込み中のオブジェクトを標準出力しながら処理することができない # Set-Content は、-PassThru スイッチにより書き込み中のオブジェクトを標準出力しながら処理可能 1..10 | Set-Content contentPassThru.log -PassThru 1..10 | Add-Content contentPassThruAdd.log -PassThru
-PassThru を付ければ実行状態が見えます。
PS > 1..10 | Set-Content contentPassThru.log -PassThru 1 2 3 4 5 6 7 8 9 10
Credential の可否
Out-File
は-Credential
スイッチがないため、別ユーザーとしてコマンド実行ができません。*1Set-Content -Credential
やAdd-Content -Credential
で、別ユーザーとしてコマンド実行可能に見えます。が、ファイルシステムプロバイダは Credentialをサポートしないので使えません。New-PSDrive や New-SmbMapping でご利用ください
# Out-File は-Credential パラメータを持たず、別ユーザーとしての実施は不可 (Invoke-Command 使えばいい) # Set-Content は、-PassThru スイッチにより書き込み中のオブジェクトを標準出力しながら処理可能 1..10 | Set-Content contentPassThru.log -Credential (Get-Credential)
Include/Exclude の可否
私は使わないです。
Out-File は、
-Includeや
-Exclude` スイッチがないため、対象フォルダにあるファイル指定などができませんSet-Content -Include
やAdd-Content -Include
あるいは-Exclude
スイッチが利用できます
# Out-Fileは、 -Include/-Exclude スイッチがない Get-Childitem hoge | Set-Content contentInclude -Include "*.log"
Filter の可否
私は使わないです。
Out-File
は-Filter パラメータを持たず、対象のプロバイダに合わせたフィルタをかけることはできませんSet-Content -Filter
やAdd-Content -Filter
でフィルタが可能です
# -Filter パラメータを持たず、対象のプロバイダに合わせたフィルタをかけることはできない。
Transaction の可否
私は使わないです。
Out-File
は、-Transaction スイッチがなく、Transaction管理外ですSet-Content -Transaction
やAdd-Content -Transaction
は、Transaction有効中にトランザクション管理に入る、ように見えてファイルシステムプロバイダはトランザクションをサポートしていないです。レジストリプロバイダのみがTransactionを利用可能です。使えない
However, the lock is a feature of the database. It is not related to transactions. If you are working in a transaction-enabled file system or other data store, the data can be changed while the transaction is in progress.
*1:Invoke-Command などで代行すればいいだけですが