ファイルの連結について、いい記事があります。
私が書くコードではSet-Content/Add-Contentを使わないです。が、なぜなのかを振り返るのもいいでしょう。
今回は、 Set-Content/Add-ContentとOut-File/Out-File -Appendの違いについてです。
- まとめ
- 上書きと追記
- 書き込み方法の違い と NoClobber による上書き防止
- Write/Read ロック
- デフォルトEncoding の違い
- InputObjectが空の場合のファイル作成挙動
- PassThru の可否
- Credential の可否
- Include/Exclude の可否
- Filter の可否
- Transaction の可否
まとめ
記事で紹介したコードは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スイッチで、ファイルがすでに存在した場合は上書きしない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で、別ユーザーとしてコマンド実行可能に見える。*2
# 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有効中はトランザクション管理下に見えるが、ファイルシステムプロバイダはトランザクションをサポートしていない。*3
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.