👴
[PowerShell]共通化したFunctionのサンプルコードを紹介
概要
主にZennの記事で自作したFunctionやサンプルとしてPowerShellスクリプトを紹介してきました。
それら記事の中で共通化(汎用的に使用)したFunctionをこの記事に集約。
PowerShellスクリプトを作成する際の参考にどうぞ!
この記事のターゲット
- PowerShellユーザーの方
- 頻繁に書く処理を共通化したい方
- 共通化したFunctionのサンプルコードを参考にしたい方
Function一覧
個別の記事で紹介したFunction
[PowerShell]複数のサーバーに対し指定したポートで疎通確認する方法
ジャグ配列のデータを元にポート疎通を確認
# Function 定義
function Check-PortMultipleServers {
param (
[Parameter(Mandatory=$true)][System.String[][]]$ServerPortPairs
)
$tempStrings=New-Object System.Text.StringBuilder
for($i = 0; $i -lt $ServerPortPairs.Length; $i++) {
$currentPair = $ServerPortPairs[$i]
try {
Test-NetConnection $currentPair[0] -Port $currentPair[1]
}
catch {
$tempStrings=New-Object System.Text.StringBuilder
@("Test-NetConnectionコマンドレットでエラーが発生しました。`r`n",`
"エラーとなったコマンド:[Test-NetConnection $($currentPair[0]) -Port $($currentPair[1])]")|
ForEach-Object{[void]$tempStrings.Append($_)}
[System.String]$messageError = $tempStrings.ToString()
Write-Error $messageError
}
}
}
Function実行
# Function 実行
[System.String[][]]$ServerPortPairs = @(
@('localhost','8080'),
@('localhost','8000'),
@('google.com','443'),
@('smtp.google.com','25')
)
Check-PortMultipleServers $ServerPortPairs
# 実行結果
警告: TCP connect to (::1 : 8080) failed
警告: TCP connect to (127.0.0.1 : 8080) failed
ComputerName : localhost
RemoteAddress : ::1
RemotePort : 8080
InterfaceAlias : Loopback Pseudo-Interface 1
SourceAddress : ::1
PingSucceeded : True
PingReplyDetails (RTT) : 0 ms
TcpTestSucceeded : False
ComputerName : localhost
RemoteAddress : ::1
RemotePort : 8000
InterfaceAlias : Loopback Pseudo-Interface 1
SourceAddress : ::1
TcpTestSucceeded : True
ComputerName : google.com
RemoteAddress : 142.251.42.142
RemotePort : 443
InterfaceAlias : イーサネット
SourceAddress : XXX.XXX.XXX.XXX
TcpTestSucceeded : True
ComputerName : smtp.google.com
RemoteAddress : 64.233.187.27
RemotePort : 25
InterfaceAlias : イーサネット
SourceAddress : XXX.XXX.XXX.XXX
TcpTestSucceeded : True
[PowerShell]挿入するタブ数を制御しインデントを合わせ配列を一括出力する方法
配列データを表示する際にインデントを合わせて出力
function Show-StringsWithByteCount {
param (
[Parameter(Mandatory=$true)][System.Object[]]$StringArray
)
[System.Object[]]$byteCounts = $StringArray.Clone()
[System.Int32]$i = 0
[System.Int32]$maxLength = 0
for ($i = 0; $i -lt $byteCounts.Count; $i++) {
$byteCounts[$i] = [System.Text.Encoding]::GetEncoding("shift_jis").GetByteCount($byteCounts[$i])
if ($maxLength -lt $byteCounts[$i]) {
$maxLength = $byteCounts[$i]
}
}
# タブ数の計算 と コンソール出力
[System.Int32]$tabCount = 0
[System.Int32]$tabWidth = 8
Write-Host ''
Write-Host ' ============ 出力結果 ============ '
Write-Host ''
for ($i = 0; $i -lt $byteCounts.Count; $i++) {
# タブの数を計算します
# 最長の文字列との差分をタブの幅で割り、切り上げます
$tabCount = [Math]::Ceiling(($maxLength - [System.Int32]$byteCounts[$i]) / $tabWidth)
# 最低1つはタブを挿入するよう設定
if ($tabCount -eq 0) {
$tabCount = 1
}
# 最終桁に改行なしで先頭部の文字列を出力
Write-Host "文字列 [$($StringArray[$i])]" -NoNewline
Write-Host ("`t" * $tabCount) -NoNewline
# 最終桁に改行ありで続く文字列を出力
Write-Host "、バイト数(Shift-JIS)[$($byteCounts[$i])] byte"
}
Write-Host ''
Write-Host ' ================================ '
Write-Host ''
Write-Host ''
}
Function実行
# Function実行
$stringLists = @("1234567890", "あ", "ア", "123456789012345678901234567890", "abcdefg")
Show-StringsWithByteCount $stringLists
# 実行結果
============ 出力結果 ============
文字列 [1234567890] 、バイト数(Shift-JIS)[10] byte
文字列 [あ] 、バイト数(Shift-JIS)[2] byte
文字列 [ア] 、バイト数(Shift-JIS)[1] byte
文字列 [123456789012345678901234567890] 、バイト数(Shift-JIS)[30] byte
文字列 [abcdefg] 、バイト数(Shift-JIS)[7] byte
================================
PowerShellで文字列のバイト数を取得する方法(文字列抽出するFunctionも紹介)
対象文字列のバイト数(Shift JIS)を取得
#################################################################################
# 処理名 | Get-SjisByteCount
# 機能 | 文字列全体のバイト数をShift JISで取得
#--------------------------------------------------------------------------------
# 戻り値 | Int32(文字列のバイト数)
# 引数 | TargetString: 対象文字列
#################################################################################
function Get-SjisByteCount {
param (
[Parameter(Mandatory=$true)][System.String]$TargetString
)
# 文字コードをSJISで設定
$sjisEncoding = [System.Text.Encoding]::GetEncoding("Shift_JIS")
# 文字列のバイト数を返す
return $sjisEncoding.GetByteCount($TargetString)
}
Functiion実行
# Function実行
Get-SjisByteCount '項目0001 '
# 実行結果
10
バイト数を指定して文字列抽出
#################################################################################
# 処理名 | Get-SubstringByByte
# 機能 | バイト数で文字列を抽出
#--------------------------------------------------------------------------------
# 戻り値 | String(抽出した文字列)
# 引数 | TargetString: 対象文字列
# | ByteStart : 抽出開始するバイト位置
# | ByteLength : 指定バイト数
#################################################################################
function Get-SubstringByByte {
param (
[Parameter(Mandatory=$true)][System.String]$TargetString,
[Parameter(Mandatory=$true)][System.Int32]$ByteStart,
[Parameter(Mandatory=$true)][System.Int32]$ByteLength
)
$sjisEncoding = [System.Text.Encoding]::GetEncoding("Shift_JIS")
# 文字列をバイト配列に変換
[System.Byte[]]$stringBytes = $sjisEncoding.GetBytes($TargetString)
# 抽出するバイト配列を初期化
$substringBytes = New-Object Byte[] $ByteLength
# 指定されたバイト位置からバイト配列を抽出
[System.Array]::Copy($stringBytes, $ByteStart, $substringBytes, 0, $ByteLength)
# 抽出したバイトデータを文字列として返す
return $sjisEncoding.GetString($substringBytes)
}
Function実行
# Function実行
# 4バイト目と5バイト目の間($start = 4)を開始位置として4バイト分($length = 4)の文字列を抽出
Get-SubstringByByte '1234あか' 4 4
# 結果「あか」
“ping -t”のようにPowerShellでも連続してping疎通する方法
PowerShellで連続Ping疎通を実行
# Functionの定義
function Invoke-ContinuousPing{
param(
[System.String]$TargetHost = 'localhost',
[System.Int32]$PingInterval = 1000
)
# ブレーク送信されるまで繰り返し
while ($true) {
try {
# ping実行
# [Microsoft.PowerShell.Commands.TestConnectionCommand+PingStatus]
$pingResult = Test-Connection $TargetHost -Count 1 -ErrorAction Stop
# 画面に結果表示
[System.String]$currentDatetime = (Get-Date -Format "yyyy/MM/dd HH:mm:ss.fff")
# ResponseTimeが0の場合、nullになるため判定する
if ($null -eq $pingResult.ResponseTime) {
# ResponseTImeがnullの場合は、固定値"0 ms"
Write-Output "$currentDatetime, $TargetHost, 0 ms"
}
else {
# nullではない場合、取得した値を設定
Write-Output "$currentDatetime, $TargetHost, $($pingResult.ResponseTime) ms"
}
}
catch {
Write-Host "エラー:$($_.Exception.Message)" -ForegroundColor Red
# 「ping -t」の挙動に合わせ、ping疎通が取れなくても処理を続行する。
# もし、NGとなった場合に処理を中断したい場合は、ここで「break」を実行。
# break
}
# 間隔をあける
Start-Sleep -Milliseconds $PingInterval
}
}
Function実行
# Function実行
Invoke-ContinuousPing www.bing.com
# 実行結果
2024/05/17 12:21:53.557, www.bing.com, 28 ms
2024/05/17 12:21:54.643, www.bing.com, 21 ms
2024/05/17 12:21:55.703, www.bing.com, 21 ms
2024/05/17 12:21:56.821, www.bing.com, 78 ms
2024/05/17 12:21:57.884, www.bing.com, 21 ms
2024/05/17 12:21:58.949, www.bing.com, 22 ms
2024/05/17 12:22:00.019, www.bing.com, 21 ms
2024/05/17 12:22:01.143, www.bing.com, 92 ms
2024/05/17 12:22:02.212, www.bing.com, 21 ms
2024/05/17 12:22:03.289, www.bing.com, 21 ms
2024/05/17 12:22:04.342, www.bing.com, 21 ms
[PowerShell]複数コマンド格納した文字列配列を一つひとつ実行する方法
引数で指定した文字列配列内のコマンドを実行
# 関数として定義
function Invoke-MultipleCommands {
param (
# 必須項目:実行するコマンドレットがある文字列配列用のパラメーター
[Parameter(Mandatory=$true)][System.String[]]$CommandList
)
# 配列内のコマンドを繰り返し処理で実行
foreach ($stringCommand in $CommandList) {
# 文字列のコマンドをスクリプトブロックに変換
$scriptblockCommand = [ScriptBlock]::Create($stringCommand)
# スクリプトブロックを実行
Invoke-Command -ScriptBlock $scriptblockCommand
}
}
Function実行
# Function実行
Invoke-MultipleCommands -CommandList @('Get-Date', 'Get-Item .\', 'Get-PSDrive C')
# 実行悔過
2024年5月17日 13:33:34
PSPath : Microsoft.PowerShell.Core\FileSystem::C:\Users\"ユーザー名"
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\Users
PSChildName : "ユーザー名"
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : True
Name : "ユーザー名"
FullName : C:\Users\"ユーザー名"
Parent : Users
Exists : True
Root : C:\
Extension :
CreationTime : 2024/03/13 10:05:45
CreationTimeUtc : 2024/03/13 1:05:45
LastAccessTime : 2024/05/17 13:33:25
LastAccessTimeUtc : 2024/05/17 4:33:25
LastWriteTime : 2024/05/16 16:43:59
LastWriteTimeUtc : 2024/05/16 7:43:59
Attributes : Directory
Mode : d-----
BaseName : "ユーザー名"
Target : {}
LinkType :
CurrentLocation : Users\"ユーザー名"
Name : C
Provider : Microsoft.PowerShell.Core\FileSystem
Root : C:\
Description : OS
MaximumSize :
Credential : System.Management.Automation.PSCredential
DisplayRoot :
Used : 113491873792
Free : 26460512256
PowerShellで 文字コード および BOM付き を判定するFunction
PowerShellで現在の文字コードを確認
# 文字コードの判定
function Get-PsEncoding {
param (
[Parameter(Mandatory=$true)][System.String]$TargetFile
)
$streamReader = New-Object System.IO.StreamReader (Convert-Path $TargetFile)
$profileEncoding = $streamReader.CurrentEncoding
$streamReader.Close()
Write-Host "EncodingName: [$($profileEncoding.EncodingName)]"
}
Function実行
# Function実行
Get-PsEncoding .\utf8.txt
# 実行結果
EncodingName: [Unicode (UTF-8)]
UTFの文字コードがBOM付きか判定
function Check-BOMStatus {
param (
[Parameter(Mandatory=$true)][System.String]$TargetFile
)
# 無効なファイルだった場合
if (-Not (Test-Path $TargetFile)) {
Write-Warning 'The target file does not exist.' -ForegroundColor Red
return
}
# BOMのバイトシーケンス
$UTF7_BOM1 = [System.Byte[]](0x2B,0x2F,0x76,0x38)
$UTF7_BOM2 = [System.Byte[]](0x2B,0x2F,0x76,0x39)
$UTF7_BOM3 = [System.Byte[]](0x2B,0x2F,0x76,0x2B)
$UTF7_BOM4 = [System.Byte[]](0x2B,0x2F,0x76,0x2F)
$UTF8_BOM = [System.Byte[]](0xEF,0xBB,0xBF)
$UTF16BE_BOM = [System.Byte[]](0xFE,0xFF)
$UTF16LE_BOM = [System.Byte[]](0xFF,0xFE)
$UTF32BE_BOM = [System.Byte[]](0x00,0x00,0xFE,0xFF)
$UTF32LE_BOM = [System.Byte[]](0xFF,0xFE,0x00,0x00)
# 先頭行をバイトで読み込み先頭から3バイト分のデータを取得
[System.Byte[]]$first4Bytes = (Get-Content -Path $TargetFile -Encoding Byte -TotalCount 4)
[System.Byte[]]$first3Bytes = $first4Bytes[0..2]
[System.Byte[]]$first2Bytes = $first4Bytes[0..1]
# 先頭バイトでBOM付きか判定
# UTF-7
if (($null -eq (Compare-Object $first4Bytes $UTF7_BOM1 -SyncWindow 0)) -Or
($null -eq (Compare-Object $first4Bytes $UTF7_BOM2 -SyncWindow 0)) -Or
($null -eq (Compare-Object $first4Bytes $UTF7_BOM3 -SyncWindow 0)) -Or
($null -eq (Compare-Object $first4Bytes $UTF7_BOM4 -SyncWindow 0))) {
Write-Host "[$($TargetFile)] is UTF-7 BOM."
}
# UTF-8
elseif ($null -eq (Compare-Object $first3Bytes $UTF8_BOM -SyncWindow 0)) {
Write-Host "[$($TargetFile)] is UTF-8 BOM."
}
# UTF-16 BE
elseif ($null -eq (Compare-Object $$first2Bytes $UTF16BE_BOM -SyncWindow 0)) {
Write-Host "[$($TargetFile)] is UTF-16 BE BOM."
}
# UTF-16 LE
elseif ($null -eq (Compare-Object $$first2Bytes $UTF16LE_BOM -SyncWindow 0)) {
Write-Host "[$($TargetFile)] is UTF-16 LE BOM."
}
# UTF-32 BE
elseif ($null -eq (Compare-Object $first4Bytes $UTF32BE_BOM -SyncWindow 0)) {
Write-Host "[$($TargetFile)] is UTF-32 BE BOM."
}
# UTF-32 LE
elseif ($null -eq (Compare-Object $first4Bytes $UTF32LE_BOM -SyncWindow 0)) {
Write-Host "[$($TargetFile)] is UTF-32 LE BOM."
}
else {
Write-Host "[$($TargetFile)] is not BOM." -ForegroundColor Red
}
}
Function実行
# Function実行
Check-BOMStatus .\utf8_bom.txt
# 実行結果
[.\utf8_bom.txt] is UTF-8 BOM.
[PowerShell]変数のデータ型を簡単に確認できるFunction
変数のデータ型を確認
function Get-Datatype {
param (
[Parameter(Mandatory=$true)]$Variable
)
# 文字列配列を宣言
[System.String[]]$rowData = @(
$Variable.GetType().BaseType.FullName, # 変数のベースタイプ
$Variable.GetType().FullName # 変数のデータ型
)
# PSCustomObjectで項目名を設定
$typesTable = [PSCustomObject]@{
BaseType = $rowData[0]
DataType = $rowData[1]
}
# コンソールで表示
$typesTable | Format-Table -Property BaseType, DataType -AutoSize -Wrap
}
Function実行
$lists = @(
@('AAAA',1),
@('BBBB',2),
@('CCCC',3)
)
# Function実行
Get-Datatype $lists
# 実行結果
BaseType DataType
-------- --------
System.Array System.Object[]
[PowerShell]文字コードと管理者権限を判定しウィンドウタイトルを変更
PowerShellウィンドウのタイトルに“文字コード”と“管理者権限の有無”を追加
# 管理者として実行しているか確認(Trueの場合、“管理者として実行”していると判断)
function Test-IsAdmin {
$winId = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$winPrincipal = new-object System.Security.Principal.WindowsPrincipal($winId)
$adminPermission = [System.Security.Principal.WindowsBuiltInRole]::Administrator
return $winPrincipal.IsInRole($adminPermission)
}
# 現在、設定している文字コードを取得
function Get-PsCharcode {
[System.String[]]$psCharcode = @(
# コンソールに出力する文字コードの規定値
($PSDefaultParameterValues['*:Encoding']),
# PowerShellから外部プログラムに渡す文字エンコードの設定
($global:OutputEncoding).WebName,
# PowerShellのコンソールに出力する文字エンコードの設定
([console]::OutputEncoding).WebName
)
return $psCharcode
}
# PowerShellウィンドウのタイトル変更
function Change-WindowTitle {
# 区切り文字の設定
[System.String]$separator1 = '|'
[System.String]$separator2 = ';'
# 現在のタイトルを取得
[System.String]$title = $Host.UI.RawUI.WindowTitle
[System.String]$baseTitle = $title
# すでにこのFunctionでタイトル変更している場合、一番左にある元のタイトル名のみ抽出
[System.String[]]$titleArray = $title.Split($separator1)
if ($titleArray.Length -ne 0) {
$baseTitle = ($titleArray[0]).TrimEnd()
}
# 現在の文字コードを取得
[System.String[]]$psCharcode = Get-PsCharcode
# 管理者として実行しているかにより設定するタイトル名を分岐
[System.String]$changeTitle = $baseTitle
if (Test-IsAdmin) {
# PowerShellを管理者として実行している場合
$changeTitle = "$baseTitle $separator1 " +
"DefaultParameter=[$($psCharcode[0])] $separator2 " +
"GlobalEncoding=[$($psCharcode[1])] $separator2 " +
"ConsoleEncoding=[$($psCharcode[2])] $separator2 " +
"#Administrator"
}
else {
# していない場合
$changeTitle = "$baseTitle $separator1 " +
"DefaultParameter=[$($psCharcode[0])] $separator2 " +
"GlobalEncoding=[$($psCharcode[1])] $separator2 " +
"ConsoleEncoding=[$($psCharcode[2])] $separator2 " +
"#Administrator"
}
$Host.UI.RawUI.WindowTitle = $changeTitle
}
Function実行
# Function実行
Change-WindowTitle
# 実行結果(PowerShellウィンドウのタイトルを確認)
Windows PowerShell | DefaultParameter=[] ; GlobalEncoding=[us-ascii] ; ConsoleEncoding=[shift_jis] ; #Administrator
[PowerShell]wingetの結果を変数に代入し出力すると文字化けが発生
PowerShellの文字コードを設定
function Set-PsOutputEncoding {
param (
[System.String]$CharCode = 'reset_encoding'
)
switch ($CharCode) {
# 文字エンコードをUTF8に設定する
'utf8' {
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
$global:OutputEncoding = [System.Text.Encoding]::UTF8
[console]::OutputEncoding = [System.Text.Encoding]::UTF8
}
# 文字エンコードをShift JIS(SJIS)に設定する
'sjis' {
# $PSDefaultParameterValues['*:Encoding'] = 'default'について
# この設定はCore以外(5.1以前)の環境でのみShift JISで設定される。
# Core環境のデフォルト値は、UTF-8でありUTF-8で設定されてしまう。
# また、Shift JISのパラメーターも存在しない為、Core環境でShift JISの設定は不可となる。
$PSDefaultParameterValues['*:Encoding'] = 'default'
$global:OutputEncoding = [System.Text.Encoding]::GetEncoding('shift_jis')
[console]::OutputEncoding = [System.Text.Encoding]::GetEncoding('shift_jis')
}
# 文字エンコードをASCIIに設定する
'ascii' {
$PSDefaultParameterValues.Remove('*:Encoding')
$global:OutputEncoding = [System.Text.Encoding]::ASCII
[console]::OutputEncoding = [System.Text.Encoding]::ASCII
}
# デフォルトパラメータの文字エンコード指定を解除する
'rm_encoding' {
$PSDefaultParameterValues.Remove('*:Encoding')
}
# 文字エンコード設定を初期状態に戻す
'reset_encoding' {
$PSDefaultParameterValues.Remove('*:Encoding')
if ($PSVersionTable.PSEdition -eq 'Core') {
# Core の場合
$global:OutputEncoding = [System.Text.Encoding]::UTF8
[console]::OutputEncoding = [System.Text.Encoding]::GetEncoding('shift_jis')
}
else {
# Core 以外の場合(PowerShell 5.1 以前)
$global:OutputEncoding = [System.Text.Encoding]::ASCII
[console]::OutputEncoding = [System.Text.Encoding]::GetEncoding('shift_jis')
}
}
}
}
Function実行
# Function実行
Set-PsOutputEncoding 'utf8'
# 実行結果(Change-WindowTitleでタイトルを確認)
Windows PowerShell | DefaultParameter=[utf8] ; GlobalEncoding=[utf-8] ; ConsoleEncoding=[utf-8] ; #Administrator
PowerShellからPythonのchardetを使って文字コードを判定
文字列判定可能なPythonのライブラリ「chardet」をPowerShellから呼び出す
function Get-PyEncoding {
param (
[Parameter(Mandatory=$true)][System.String]$TargetFile
)
# python インストール確認
if (-Not(Get-Command 'python' -ErrorAction SilentlyContinue)) {
Write-Host 'Python is not install.' -ForegroundColor Red
return
}
# 存在チェック
if (-Not(Test-Path $TargetFile)) {
Write-Host "[$TargetFile] does not exist." -ForegroundColor Red
return
}
# 絶対パスに変換
[System.String]$fullPath = (Convert-Path $TargetFile)
# データ種類のチェック
if (-Not(Test-Path $fullPath -PathType Leaf)) {
Write-Host "[$fullPath] is not a file." -ForegroundColor Red
return
}
# Pythonスクリプトのコード
[System.String[]]$pySource =
@"
import subprocess
import sys
# chardet がインストールされていない場合はインストールしてからインポート
try:
import chardet
except ImportError:
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'chardet', '--user'])
import chardet
# 文字コードを判定するFunction
def determine_encoding(file_path):
with open(file_path, 'rb') as file:
raw_data = file.read()
result = chardet.detect(raw_data)
print(f"Detected encoding for [{file_path}] is {result['encoding']} with {result['confidence']*100}% confidence.")
# コマンドラインの引数を取得
file_path = sys.argv[1]
# 対象ファイルの文字列を判定
determine_encoding(file_path)
"@ -split "`r`n"
# Pythonスクリプトの準備
# Pythonスクリプトを格納するフォルダーがない場合は、新規作成
[System.String]$pyFolderPath = "$PROFILE\..\user-defined-py"
if (-Not(Test-Path $pyFolderPath)) {
New-Item -Path $pyFolderPath -ItemType 'directory' -Force > $null
}
$pyFolderPath = (Convert-Path $pyFolderPath)
# 今回実行するPythonスクリプトがない場合は、新規作成
[System.String]$pyScriptPath = "$pyFolderPath\chardet_runner.py"
if (-Not(Test-Path $pyScriptPath)) {
$utf8Encoding = New-Object System.Text.UTF8Encoding
[System.IO.File]::WriteAllText($pyScriptPath, $pySource, $utf8Encoding)
}
# Pythonスクリプトの実行
try {
& python $pyScriptPath $fullPath
}
catch {
Write-Warning 'Python script execution error.'
return
}
}
Function実行
# Function実行
Get-PyEncoding .\utf16.txt
# 実行結果
Detected encoding for [D:\Downloads\utf16.txt] is ascii with 100.0% confidence.
PowerShellでファイル内の改行コードを一括変換するFunction
ファイル内の改行コードを一括変換
# 改行コードを可視化するFunction
function Show-Returncode {
param (
[Parameter(Mandatory=$true)][System.String]$TargetFile,
[ValidateSet('CRLF', 'LF')][System.String]$Returncode = 'CRLF'
)
[System.Collections.Hashtable]$ReturncodeRegex = @{
'CR' = "`r"
'LF' = "`n"
'CRLF' = "`r`n"
}
[System.Collections.Hashtable]$ReturncodeMark = @{
'CR' = '<CR>'
'LF' = '<LF>'
'CRLF' = '<CRLF>'
}
# コンソールに表示する際の改行コードを追加。改行コードは引数で指定した CRLF か LF が入る。
[System.Collections.Hashtable]$ReturncodeVisualize = @{
'CR' = "<CR>$($ReturncodeRegex[$Returncode])"
'LF' = "<LF>$($ReturncodeRegex[$Returncode])"
'CRLF' = "<CRLF>$($ReturncodeRegex[$Returncode])"
}
# 改行コードをマークに変換
[System.String]$targetData = (Get-Content -Path $TargetFile -Raw)
$targetData = $targetData -Replace $ReturncodeRegex['CRLF'], $ReturncodeMark['CRLF']
$targetData = $targetData -Replace $ReturncodeRegex['LF'], $ReturncodeMark['LF']
$targetData = $targetData -Replace $ReturncodeRegex['CR'], $ReturncodeMark['CR']
# マークからマーク+改行コード(コンソール表示用)に変換
$targetData = $targetData -Replace $ReturncodeMark['CRLF'], $ReturncodeVisualize['CRLF']
$targetData = $targetData -Replace $ReturncodeMark['LF'], $ReturncodeVisualize['LF']
$targetData = $targetData -Replace $ReturncodeMark['CR'], $ReturncodeVisualize['CR']
Write-Host ''
Write-Host ' *-- Result: Show-Returncode ---------------------------------------------* '
Write-Host $targetData
Write-Host ' *----------------------------------------------------------------------------* '
Write-Host ''
Write-Host ''
}
# 改行コードを変換するFunction
function Replace-Returncode {
param (
# 必須:変換対象のファイルを指定
[Parameter(Mandatory=$true)][System.String]$TargetFile,
# 必須:変換する前後の改行コードを指定
[Parameter(Mandatory=$true)][ValidateSet('CR', 'LF', 'CRLF')][System.String]$BeforeReturncode,
[Parameter(Mandatory=$true)][ValidateSet('CR', 'LF', 'CRLF', 'NONE')][System.String]$AfterReturncode,
# 任意:変換後のファイルを別ファイルで保存したい場合に保存先を指定
[System.String]$Destination='',
# 任意:変換後のファイルを可視化しコンソール表示したい場合に指定
[System.Boolean]$Show=$false
)
# Before・Afterが異なる改行コードを指定しているかチェック
if ($BeforeReturncode -eq $AfterReturncode) {
Write-Warning '引数で指定された 変換前 と 変換後 の改行コードが同一です。引数を見直してください。'
return
}
# ファイルが存在しない場合
if (-Not(Test-Path $TargetFile)) {
Write-Warning '変換対象のファイルが存在しません。処理を中断します。'
return
}
# ファイルの中身がない場合
[System.String]$beforeData = (Get-Content -Path $TargetFile -Raw)
if ($null -eq $beforeData) {
Write-Warning '変換対象のファイル内容が空です。処理を中断します。'
return
}
# 改行コードのハッシュテーブル作成
[System.Collections.Hashtable]$ReturncodeRegex = @{
'CR' = "`r"
'LF' = "`n"
'CRLF' = "`r`n"
'NONE' = ''
}
# 指定した変換前後の改行コードを正規表現の表記に変更
[System.String]$beforeReturncodeRegex = $ReturncodeRegex[$BeforeReturncode]
[System.String]$afterReturncodeRegex = $ReturncodeRegex[$AfterReturncode]
# 変換処理
[System.String]$afterData = ($beforeData -Replace $beforeReturncodeRegex, $afterReturncodeRegex)
# 変換されなかった場合
if ($null -eq (Compare-Object $beforeData $afterData -SyncWindow 0)) {
Write-Warning '処理を実行しましたが、対象の改行コードがなく変換されませんでした。処理を終了します。'
return
}
# 保存先の指定がない場合、上書き保存
if ($Destination -eq '') {
$Destination = $TargetFile
Write-Host ''
Write-Host '上書き保存します。'
}
# 保存先が指定されている場合、別ファイルで保存(名前を付けて保存)
else {
if (Test-Path $Destination -PathType Leaf) {
Write-Warning '指定の保存場所には、すでにファイルが存在します。処理を中断します。'
return
}
if (-Not(Test-Path "$Destination\.." -PathType Container)) {
Write-Warning '保存場所のフォルダーが存在しません。処理を中断します。'
return
}
Write-Host ''
Write-Host '名前を付けて保存します。'
}
# 保存
Try {
Set-Content -Path $Destination -Value $afterData -NoNewline
}
catch {
Write-Error 'Replace-Returncodeの保存処理でエラーが発生しました。処理を中断します。'
return
}
[System.String]$savepath_full = Convert-Path $Destination
Write-Host " 保存先: [$savepath_full]"
Write-Host ''
Write-Host ''
# 表示
if ($Show) {
Show-Returncode($Destination)
}
}
D:\Downloads\utf8.txt
を対象に改行コードを CRLF から LF に変換します。
オプション「-Show」を $True
でコマンド実行している為、自動的にFunction Show-Returncode
が呼び出されて、
変換したファイルの改行コードを可視化しコンソールに表示します。
Function実行
PS D:\Downloads> Show-Returncode .\utf8.txt
*-- Result: Show-Returncode ---------------------------------------------*
test<CRLF>
*----------------------------------------------------------------------------*
PS D:\Downloads>
PS D:\Downloads> Replace-Returncode .\utf8.txt CRLF LF .\utf8_lf.txt $true
名前を付けて保存します。
保存先: [D:\Downloads\utf8_lf.txt]
*-- Result: Show-Returncode ---------------------------------------------*
test<LF>
*----------------------------------------------------------------------------*
PS D:\Downloads>
複数IDを検索キーにwinget showの結果を抽出し表示するFunction
「winget show」の結果から対象IDの情報を抽出
# Function
function Write-WingetShow {
param (
[System.String[]]$TargetIDs
)
# 抽出対象の文字列を指定
[System.String]$hitMessage = '見つかりました '
[System.String]$installerType = ' インストーラーの種類: '
# 文字コードをUTF-8に変更
[console]::OutputEncoding = [System.Text.Encoding]::UTF8
# コマンド結果から抽出しアプリ名・ID・ソースのみを表示
foreach ($id in $TargetIDs) {
[System.String[]]$appInfo = (winget show --id $id)
# 検索結果あり
if ($appInfo -match $hitMessage) {
$appNameAndId = $appInfo | Select-String "$hitMessage.*" | ForEach-Object {
$_.Matches[0].Value -replace "$hitMessage", ''
}
$appSource = $appInfo | Select-String "$installerType.*" | ForEach-Object {
$_.Matches[0].Value -replace "$installerType", ''
}
Write-Host "$appNameAndId [$appSource]"
}
# 検索結果なし
else {
Write-Warning "見つかりませんでした。ID: [$id]"
# 中断する場合はコメントアウト解除
#return
}
}
}
Function実行
# 検索対象のIDを配列で指定
$searchIDs = @(
'Microsoft.Edge',
'1234567890AB',
'XPFFZHVGQWWLHB',
'Microsoft.VCLibs.Desktop.14'
)
# Function の実行
Write-WingetShow $searchIDs
# 実行結果
Microsoft Edge [Microsoft.Edge] [msi]
警告: 見つかりませんでした。ID: [1234567890AB]
OneNote [XPFFZHVGQWWLHB] [exe]
Microsoft Visual C++ 2015 UWP Desktop Runtime Package [Microsoft.VCLibs.Desktop.14] [msix]
PowerShell 6.0以降(Core)でOS環境を確認できる自動変数
PowerShell実行環境をチェック
# エラーコード enum設定
Add-Type -TypeDefinition @"
public enum MESSAGECODE {
Successful = 0,
Error_NotCore,
Error_NotSupportedVersion,
Error_NotWindows
}
"@
#################################################################################
# 処理名 | Check-PsEnv
# 機能 | PowerShell環境チェック
#--------------------------------------------------------------------------------
# 戻り値 | MESSAGECODE(enum)
# 引数 | なし
#################################################################################
function Check-PsEnv {
[MESSAGECODE]$returncode = [MESSAGECODE]::Successful
# 環境情報を取得
[System.Collections.Hashtable]$psVersion = $PSVersionTable
# 環境の判定:Coreではない場合(5.1だと'Desktop'となる)
if ($psVersion.PSEdition -ne 'Core') {
$returncode = [MESSAGECODE]::Error_NotCore
Write-Host 'Core(6.0以降)の環境ではない' -ForegroundColor Red
}
# 環境の判定:メジャーバージョンが7より小さい場合
elseif ($psVersion.PSVersion.Major -lt 7) {
$returncode = [MESSAGECODE]::Error_NotSupportedVersion
Write-Host 'Core(6.0以降)の環境だが、7以上 の環境ではない' -ForegroundColor Red
}
# 環境の判定:Windows OSではない場合(PowerShell Coreのみ使用できる自動変数)
elseif (-Not($IsWindows)) {
$returncode = [MESSAGECODE]::Error_NotWindows
Write-Host 'Core(6.0以降)の環境で、かつ 7以上 の環境だが、Windows OS の環境ではない' -ForegroundColor Red
}
else {
Write-Host 'Core(6.0以降)の環境で、かつ 7以上 の環境、Windows OS の環境である'
}
return $returncode
}
[PowerShell]配列の種類(ジャグ配列 or 多次元配列)を判定するFunction"
2024.6.13 追記
引数の配列の種類を判定するFunction
function Get-ArrayType {
param(
$InputObject
)
[System.Collections.Hashtable]$arrayTypes = @{
"OtherTypes" = -1
"SingleArray" = 0
"MultiLevel" = 1
"MultiDimensional" = 2
}
# データがない場合
if ($null -eq $InputObject) {
return $arrayTypes["OtherTypes"]
}
# 一番外枠が配列ではない場合
if ($InputObject -isnot [System.Array]) {
return $arrayTypes["OtherTypes"]
}
# ジャグ配列(多段階配列)か判定
$isMultiLevel = $false
foreach ($element in $InputObject) {
if ($element -is [System.Array]) {
# 配列の中も配列で多段配列
$isMultiLevel = $true
break
}
}
if ($isMultiLevel) {
return $arrayTypes["MultiLevel"]
}
# 多次元配列か判定
if ($InputObject.Rank -ge 2) {
# 2次元以上の場合
return $arrayTypes["MultiDimensional"]
}
else {
# 1次元の場合
return $arrayTypes["SingleArray"]
}
}
Function実行
# テストデータの準備
# 単一の配列
$singleArray = @('あ', 'い', 'う')
# 多段階配列
$multiLevel = @(@(1, 2, 3), @(4, 5), @(6, 7, 8, 9))
# String型の1x2 多次元配列
$stringMultiDim1x2 = New-Object 'System.String[,]' 1,2
$stringMultiDim1x2[0,0] = 'Hello'
$stringMultiDim1x2[0,1] = 'World'
# Int32型の3x2 多次元配列
$intMultiDim3x2 = New-Object 'System.Int32[,]' 3,2
$intMultiDim3x2[0,0] = 1
$intMultiDim3x2[0,1] = 2
$intMultiDim3x2[1,0] = 3
$intMultiDim3x2[1,1] = 4
$intMultiDim3x2[2,0] = 5
$intMultiDim3x2[2,1] = 6
# Object型の3x1 多次元配列
$objectMultiDim3x1 = New-Object 'System.Object[,]' 3,1
$objectMultiDim3x1[0,0] = 'I am String.'
$objectMultiDim3x1[1,0] = 1
$objectMultiDim3x1[2,0] = 10.5
# テストデータを集約
$testData = @(
@{ "Description" = "単一配列"; "InputObject" = $singleArray },
@{ "Description" = "多段階配列"; "InputObject" = $multiLevel },
@{ "Description" = "String型1x2多次元配列"; "InputObject" = $stringMultiDim1x2 },
@{ "Description" = "Int32型3x2多次元配列"; "InputObject" = $intMultiDim3x2 }
@{ "Description" = "Object型3x1多次元配列"; "InputObject" = $objectMultiDim3x1 }
)
# Functionを実行
foreach ($data in $testData) {
$result = Get-ArrayType -InputObject $data["InputObject"]
Write-Host "$($data["Description"])の結果: $result"
}
# 実行結果
単一配列の結果: 0
多段階配列の結果: 1
String型1x2多次元配列の結果: 2
Int32型3x2多次元配列の結果: 2
Object型3x1多次元配列の結果: 2
PowerShellのわかりにくい比較演算子を記号で判定可能にするFunction
2024.6.14 追記
記号の比較演算子で比較ができるFunciton
function Test-Comparison {
param(
[Parameter(Mandatory=$true)][System.Object]$Value1,
[Parameter(Mandatory=$true)]
[System.String]$Operator,
[Parameter(Mandatory=$true)]
[System.Object]$Value2
)
switch ($Operator) {
# 通常の比較演算子
'==' { return $Value1 -eq $Value2 }
'!=' { return $Value1 -ne $Value2 }
'<' { return $Value1 -lt $Value2 }
'<=' { return $Value1 -le $Value2 }
'>' { return $Value1 -gt $Value2 }
'>=' { return $Value1 -ge $Value2 }
default { throw "Invalid operator: $Operator" }
}
}
Function実行
# 例: 10 と 20 を比較して、10 が 20 より小さいかどうかを判定
$standardValue = 10
$comparedValue = 20
# そのまま実行
Write-Host '--- そのままFunctionを実行した結果 ---'
Test-Comparison -Value1 $standardValue -Operator '<' -Value2 $comparedValue
Write-Host ''
# if文で実行
Write-Host '--- if文でFunctionを実行した結果 ---'
if (Test-Comparison $standardValue '<' $comparedValue) {
Write-Host "True判定:$standardValue は $comparedValue より小さい"
Write-Host ''
}
else {
Write-Host "False判定:$comparedValue は $comparedValue より小さくない"
Write-Host ''
}
# 実行結果
--- そのままFunctionを実行した結果 ---
True
--- if文でFunctionを実行した結果 ---
True判定:10 は 20 より小さい
[PowerShell]指定したモジュールの導入有無を確認するFunction
2024.6.17 追記
モジュールの導入有無を確認
function Test-ModuleInstalled {
param (
[System.String]$ModuleName
)
$moduleInstalled = $false
# モジュール情報を取得
$module = (Get-Module -ListAvailable -Name $ModuleName)
# モジュールが導入済みの場合
if ($null -ne $module) {
$moduleInstalled = $true
}
return $moduleInstalled
}
Function実行
# 指定したモジュール「ImportExcel」が未導入の場合にインストール
$moduleName = 'ImportExcel'
if (-Not(Test-ModuleInstalled $moduleName)) {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# 管理者権限が必要
Install-Module -Name $moduleName -Scope CurrentUser -Force
Write-Host "$($moduleName) モジュールをインストールしました。"
} else {
Write-Host "$($moduleName) モジュールは既にインストールされています。"
}
# 実行結果
# 未導入でインストールした場合
ImportExcel モジュールをインストールしました。
# 導入済みだった場合
ImportExcel モジュールは既にインストールされています。
[PowerShell]ファイルのロック状態を確認するFunction
2024.6.18 追記
ファイルのロック状態を確認するFunction
function Test-FileLocked {
param (
[Parameter(Mandatory=$true)][System.String]$Path
)
if (-Not(Test-Path $Path)) {
Write-Error '対象ファイルが存在しません。' -ErrorAction Stop
}
# 相対パスだとOpenメソッドが正常動作しない為、絶対パスに変換
$fullPath = (Resolve-Path -Path $Path).Path
$fileLocked = $false
try {
# 読み取り専用でファイルを開く処理を実行
$fileStream = [System.IO.File]::Open($fullPath, 'Open', 'ReadWrite', 'None')
}
catch {
# ファイルが開けない場合、ロック状態と判断
$fileLocked = $true
}
finally {
if ($null -ne $fileStream) {
$fileStream.Close()
}
}
return $fileLocked
}
Function実行
$targetPath = 'D:\Downloads\TestBook.xlsx'
# 対象ファイルを開いていない状態 → 戻り値: False(開いていない状態)
Test-FileLocked $targetPath
# False
# 対象ファイルを開いている状態 → 戻り値: True(開いていてロック状態)
Test-FileLocked $targetPath
# True
サンプルプログラムより抜粋
[PowerShell]CDなど記憶媒体にあるファイルの文字列をチェックするツール
CEトレイ(メディアトレイ)を自動でオープン
CLI操作でYes/Noの入力待ち
CDドライブ(メディアドライブ)が認識されているか検証
ファイル名と拡張子の検証
[PowerShell]コピーバックアップ&ローテーションの方法 - Local to NAS
バックアップローテーション
ファイルのコピーバックアップ
[PowerShell]“RPA画面操作:IME辞書をテキスト出力”と“単語登録済みかチェック”を自動実行する方法
ダブルクォーテーション囲みを削除
利用中のモニターサイズから指定の数値で除算しウィンドウサイズを取得
GUI操作(Windowsフォーム)でYes/Noの入力待ち
メッセージコード(enum)を検索キーにメッセージを取得
メッセージボックスの表示
テキストファイルを検索対象に複数キーワードごとにカウント
上記で取得した配列をコンソール上に表示
一時的に保存する出力ファイルを格納するためのフォルダーを作成
[PowerShell]MySQLデータをCSVファイルで取得する方法 - DB to CSV
文字列形式の日付(yyyymmdd)が正しい値か検証
開始日付と終了日付が日付の期間として正しいか検証
MySQLデータベースへの接続処理
MySQLのSQLコマンドを実行
画像をリサイズしてWebP形式に変換するPowerShellスクリプト
まだ記事として公開していないPowerShellスクリプト。
文字列がASCII文字だけで構成(英数字だけでマルチバイト文字がない)かの有無
使用するファイル名を取得
使用するフォルダー名を取得
OCR(TesseractOCR)で出力したテキストをキーワード検索しカウント
指定ファイルの拡張子を変更
指定された画像ファイルのファイル形式をWebP形式に変換
指定された画像ファイルのリサイズ処理
[PowerShell]PDFファイル内の文字列を検索し存否を判定するツール
注意事項:使用しているライブラリ“iTextSharp 5”のライセンスは「AGPLv3」
指定キーワードでPDFファイル内を検索
[PowerShell]XMLファイルを整形(加工)するツール
XMLファイルの属性を一括チェック
XML属性の並び替え処理
XMLで不要な属性の削除
2つのテキスト形式ファイルを比較しコンソール上に出力
XMLファイル内の文字列置換
Windows OSで一括アップデートするPSスクリプト(Win Update/MS Defender/アプリ)
アクティブウィンドウの切り替え
引数で指定のモジュールをインストール
まとめ
わたし自身が新規でPowerShellスクリプトを作成する際に便利そうなので、この記事にまとめてみました。
これらFunctionをプロファイルに登録するとより便利かも。
関連記事
Discussion