💳

自作Functionを半自動でPowerShellプロファイルに定義するコード

2024/04/04に公開

概要

こちらの記事でデータ型を確認できるFunctionを自作しました。
このような自作Functionは、PowerShellのプロファイルに定義する事で簡単に呼び出すことができます。

ただ、環境が変わる度にPowerShellのプロファイルを定義する必要があった為、面倒に感じていました。

この問題を解決するため、今回は自作したFunctionを読み込み、PowerShellのプロファイルに半自動で定義するコードを作成しました。
なお、自作したFunctionを読み込む方法は、ハードコーディングしたコードを読み込む方法 と 専用フォルダー配下のps1ファイルを読み込む方法 の2通りで準備しました。

この記事のターゲット

  • PowerShellユーザーの方
  • 自作したFunctionを半自動でPowerShellプロファイル($PROFILE)に定義したい方

環境

$PSVersionTable
PS C:\Users\"ユーザー名"> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.4170
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.4170
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1


PS C:\Users\"ユーザー名">

半自動でPowerShellプロファイルを作成し自作Functionを定義するコード

冒頭に記載した通り、ハードコーディングした自作Function と 専用フォルダー配下にあるps1ファイル内の自作Function の2つから読み込み、PowerShellのプロファイルに定義します。

各々の用途にあわせてカスタマイズしてください。

テンプレート
# PowerShellプロファイルのヘッダー
[System.String[]]$header = 
@"
#################################################################################
# PowerShellプロファイル
# ($($PROFILE))
#
#################################################################################

"@

# ハードコーディングしている自作Function
[System.String[]]$ps_source = 
@'

【ここに自作Functionをハードコーディングする】

'@ -split "`r`n"

# プロファイル用のPowerShellスクリプト($PROFILE)がない場合は、新規作成
if (-Not(Test-Path $PROFILE)) {
    # LF変換
    $header = $header -Replace "`r`n", "`n"
    # 文字コードは UTF-8 BOM付き で新規作成
    $utf8Encoding = New-Object System.Text.UTF8Encoding $true
    [System.IO.File]::WriteAllText($PROFILE, $header, $utf8Encoding)
}

# プロファイル が UTF-8 BOM付き であるか確認
$UTF8_BOM = [System.Byte[]](0xEF,0xBB,0xBF)
[System.Byte[]]$first_3bytes = (Get-Content -Path $PROFILE -Encoding Byte -TotalCount 3)
if (-Not($null -eq (Compare-Object $first_3bytes $UTF8_BOM -SyncWindow 0))) {
    Write-Host "プロファイルのPowerShellスクリプト[$PROFILE]の文字コードが「UTF-8 BOM付き」ではありません。" -ForegroundColor Red
    Write-Host "処理を中断します。" -ForegroundColor Red
    return
}

# ハードコーディングした自作Function($ps_source)をプロファイルに追記
[System.String]$name = ''
[System.Boolean]$is_skip = $false
$func_lines = $ps_source -split "`n" | Where-Object { $_ -match "^Function " }
if ($func_lines.Count -gt 0) {
    foreach ($line in $func_lines) {
        $name = ($line -split ' ' | Select-Object -First 2)
        If (Select-String -Path $PROFILE -Pattern $name) {
            Write-Host 'ハードコーディングしたコード($ps_source)のFunctionがプロファイルに定義済みです。ハードコーディングした自作Functionの追記処理はスキップします。'
            Write-Host "(プロファイルに登録済みのFunction名: [$name])"
            $is_skip = $true
            break
        }
    }
    # ハードコーディングした自作Functionを追記
    if (-Not($is_skip)) {
        # LF変換
        $ps_source = $ps_source -Replace "`r`n", "`n"
        Add-Content -Path $PROFILE -Value $ps_source
    }
}

# ps1ファイルを格納する専用フォルダーがない場合は、フォルダーを作成
[System.String]$psfolder_path = "$PROFILE\..\user-defined-ps"
if (-Not(Test-Path $psfolder_path)) {
    # フォルダーを作成
    New-Item -Path $psfolder_path -ItemType 'directory' -Force > $null
}
$psfolder_path = (Convert-Path $psfolder_path)
# 専用フォルダー内の自作Functionをプロファイルに追記
$name = ''
$is_skip = $false
# 専用フォルダー配下にps1ファイルがない場合は、この処理が最終処理となるため早期リターン
if (-Not(Test-Path "$psfolder_path\*.ps1")) {
    Write-Host "専用フォルダー [$psfolder_path] にps1ファイルがありません。処理を終了します。" 
    return
}

[System.String]$psfolder_source = (Get-Content "$psfolder_path\*.ps1" -Raw)
$func_lines = $psfolder_source -split "`n" | Where-Object { $_ -match "^Function " }
if ($func_lines.Count -gt 0) {
    foreach ($line in $func_lines) {
        $name = ($line -split ' ' | Select-Object -First 2)
        If (Select-String -Path $PROFILE -Pattern $name) {
            Write-Host '専用フォルダー内のFunctionがプロファイルに定義済みです。専用フォルダーにある自作Functionの追記処理はスキップします。。'
            Write-Host "(プロファイルに登録済みのFunction名: [$name])"
            $is_skip = $true
            break
        }
    }
    # 専用フォルダーにある自作Functionの追記
    if (-Not($is_skip)) {
        # LF変換
        $psfolder_source = $psfolder_source -Replace "`r`n", "`n"
        Add-Content -Path $PROFILE -Value $psfolder_source
    }
}
実例(実際に私が作成したFunctionをハードコーディングした例)
コード(実際に私が作成したFunctionをハードコーディングした例)
# PowerShellプロファイルのヘッダー
[System.String[]]$header = 
@"
#################################################################################
# PowerShellプロファイル
# ($($PROFILE))
#
#################################################################################

"@

# ハードコーディングしている自作Function
[System.String[]]$ps_source = 
@'

#################################################################################
# 処理名  | VerificationEnv
# 機能   | PowerShell環境チェック
#          | 参考情報:https://zenn.dev/haretokidoki/articles/fac8e50fbe9dcd
#--------------------------------------------------------------------------------
# 戻り値  | MESSAGECODE(enum)
# 引数   | なし
#################################################################################
# エラーコード enum設定
Add-Type -TypeDefinition @"
    public enum MESSAGECODE {
        Successful = 0,
        Abend,
        Cancel,
        Error_NotCore,
        Error_NotSupportedVersion,
        Error_NotWindows
    }
"@
Function VerificationEnv {
    [MESSAGECODE]$return_code = [MESSAGECODE]::Successful

    # 環境情報を取得
    [System.Collections.Hashtable]$ps_ver = $PSVersionTable

    # 環境の判定:Coreではない場合(5.1だと'Desktop'となる)
    if ($ps_ver.PSEdition -ne 'Core') {
        $return_code = [MESSAGECODE]::Error_NotCore
        Write-Host 'Core(6.0以降)の環境ではない' -ForegroundColor Red
    }
    # 環境の判定:メジャーバージョンが7より小さい場合
    elseif ($ps_ver.PSVersion.Major -lt 7) {
        $return_code = [MESSAGECODE]::Error_NotSupportedVersion
        Write-Host 'Core(6.0以降)の環境だが、7以上 の環境ではない' -ForegroundColor Red
    }
    # 環境の判定:Windows OSではない場合(PowerShell Coreのみ使用できる自動変数)
    elseif (-Not($IsWindows)) {
        $return_code = [MESSAGECODE]::Error_NotWindows
        Write-Host 'Core(6.0以降)の環境で、かつ 7以上 の環境だが、Windows OS の環境ではない' -ForegroundColor Red
    }
    else {
        Write-Host 'Core(6.0以降)の環境で、かつ 7以上 の環境、Windows OS の環境である'
    }

    return $return_code
}

#################################################################################
# 処理名  | Get-Datatype
# 機能   | 変数のデータ型を確認
#          | 参考情報:https://zenn.dev/haretokidoki/articles/1632e92c37ea98
#--------------------------------------------------------------------------------
# 戻り値  | データ型を表示(項目1:BaseType、項目2:Type)
# 引数   | $variable 調査対象の変数
#################################################################################
Function Get-Datatype {
	Param (
	    [Parameter(Mandatory=$true)]$variable
	)

    # 文字列配列を宣言
	[System.String[]]$rowdata = @(
		$variable.GetType().BaseType.FullName,      # 変数のベースタイプ
		$variable.GetType().FullName                # 変数のデータ型
	)
	
    # PSCustomObjectで項目名を設定
	$types_table = [PSCustomObject]@{
		BaseType = $rowdata[0]
		DataType = $rowdata[1]
	}

    # コンソールで表示
    $types_table | Format-Table -Property BaseType, DataType -AutoSize -Wrap
}

#################################################################################
# 処理名  | Test-IsAdmin
# 機能   | PowerShellが管理者として実行しているか確認
#          | 参考情報:https://zenn.dev/haretokidoki/articles/67788ca9b47b27
#--------------------------------------------------------------------------------
# 戻り値  | Boolean(True: 管理者権限あり, False: 管理者権限なし)
# 引数   | -
#################################################################################
Function Test-IsAdmin {
    $win_id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $win_principal = new-object System.Security.Principal.WindowsPrincipal($win_id)
    $admin_permission = [System.Security.Principal.WindowsBuiltInRole]::Administrator
    return $win_principal.IsInRole($admin_permission)
}

#################################################################################
# 処理名  | GetPsCharcode
# 機能   | PowerShellコンソールの文字コードを取得
#          | 参考情報:https://zenn.dev/haretokidoki/articles/67788ca9b47b27
#--------------------------------------------------------------------------------
# 戻り値  | ps_charcode[]
#          |  - 項目01 文字エンコードを指定できるコマンドレットの既定値
#          |  - 項目02 PowerShellから外部プログラムに渡す文字エンコードの設定
#          |  - 項目01 PowerShellのコンソールに出力する文字エンコードの設定
# 引数   | -
#################################################################################
Function GetPsCharcode {
    [System.String[]]$ps_charcode = @()
    $ps_charcode = @(
        # 文字エンコードを指定できるコマンドレットの既定値
        ($PSDefaultParameterValues['*:Encoding']),
        # PowerShellから外部プログラムに渡す文字エンコードの設定
        ($global:OutputEncoding).WebName,
        # PowerShellのコンソールに出力する文字エンコードの設定
        ([console]::OutputEncoding).WebName
    )

    return $ps_charcode
}

#################################################################################
# 処理名  | ChangeWindowTitle
# 機能   | PowerShellウィンドウのタイトル変更(文字コードとPowerShellの管理者権限有無を追加)
#          | 参考情報:https://zenn.dev/haretokidoki/articles/67788ca9b47b27
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | -
#################################################################################
# PowerShellウィンドウのタイトル変更
Function ChangeWindowTitle {
    # 区切り文字の設定
    [System.String]$pos1 = '|'
    [System.String]$pos2 = ';'

    # 現在のタイトルを取得
    [System.String]$title = $Host.UI.RawUI.WindowTitle
    [System.String]$base_title = $title

    # 既にこのFunctionでタイトル変更している場合、一番左にある文字列を抽出
    [System.String[]]$title_array = $title.Split($pos1)
    if ($title_array.Length -ne 0) {
        $base_title = ($title_array[0]).TrimEnd()
    }

    # 現在の文字コードを取得しタイトルに追加
    [System.String[]]$ps_charcode = GetPsCharcode

    [System.String]$change_title = $base_title
    if (Test-IsAdmin) {
        # 管理者として実行している場合
        $change_title = $base_title + " $pos1 " +
                        "DefaultParameter='$($ps_charcode[0])'" + " $pos2 " +
                        "GlobalEncoding='$($ps_charcode[1])'" + " $pos2 " +
                        "ConsoleEncoding='$($ps_charcode[2])'" + " $pos2 " +
                        "#Administrator"
    }
    else {
        # 管理者として実行していない場合
        $change_title = $base_title + " $pos1 " +
                        "DefaultParameter='$($ps_charcode[0])'" + " $pos2 " +
                        "GlobalEncoding='$($ps_charcode[1])'" + " $pos2 " +
                        "ConsoleEncoding='$($ps_charcode[2])'" + " $pos2 " +
                        "#Not_Administrator"
    }
    $Host.UI.RawUI.WindowTitle = $change_title

    # 完了メッセージ
    Write-Host 'タイトルに“文字コード”と“管理者権限の有無”の情報を追加しました。' -ForegroundColor Cyan
}

#################################################################################
# 処理名  | SetPsOutputEncoding
# 機能   | PowerShellにおける複数の文字コード設定を一括変更
#          | 参考情報:https://zenn.dev/haretokidoki/articles/8946231076f129
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | $charcode(引数を省略した場合は、'reset_encoding'で設定)
#      |  - 'utf8'          : UTF-8に設定
#      |  - 'sjis'          : Shift-JISに設定
#      |  - 'ascii'         : US-ASCIIに設定
#      |  - 'rm_encoding'   : デフォルトパラーメーターを削除
#      |  - 'reset_encoding': 規定値に戻す
#################################################################################
Function SetPsOutputEncoding {
    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呼び出し
    ChangeWindowTitle
}

#################################################################################
# 処理名  | Get-PsEncoding
# 機能   | PowerShellでファイルの文字コードを判定
#          | 参考情報:https://zenn.dev/haretokidoki/articles/962a7fc6c51b47
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | $targetfile 対象ファイル
#################################################################################
Function Get-PsEncoding {
	Param (
		[Parameter(Mandatory=$true)][System.String]$targetfile
	)
	$stream_reader = [System.IO.StreamReader] $targetfile
	$profile_encoding = $stream_reader.CurrentEncoding
	$stream_reader.Close()

	Write-Host "EncodingName: [$($profile_encoding.EncodingName)]"
}

#################################################################################
# 処理名  | Check-BOMStatus
# 機能   | PowerShellでファイルの文字コードにおけるBOMを判定
#          | 参考情報:https://zenn.dev/haretokidoki/articles/962a7fc6c51b47
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | $targetfile 対象ファイル
#################################################################################
Function Check-BOMStatus {
    Param (
        [Parameter(Mandatory=$true)][System.String]$targetfile
    )
    if (-Not (Test-Path $targetfile)) {
        Write-Host '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[]]$first_4bytes = (Get-Content -Path $targetfile -Encoding Byte -TotalCount 4)
    [System.Byte[]]$first_3bytes = $first_4bytes[0..2]
    [System.Byte[]]$first_2bytes = $first_4bytes[0..1]
    
    # 先頭バイトでBOM付きか判定
    # UTF-7
    if (($null -eq (Compare-Object $first_4bytes $UTF7_BOM1 -SyncWindow 0)) -Or
        ($null -eq (Compare-Object $first_4bytes $UTF7_BOM2 -SyncWindow 0)) -Or
        ($null -eq (Compare-Object $first_4bytes $UTF7_BOM3 -SyncWindow 0)) -Or
        ($null -eq (Compare-Object $first_4bytes $UTF7_BOM4 -SyncWindow 0))) {
        Write-Host "[$($targetfile)] is UTF-7 BOM."
    }
    # UTF-8
    elseif ($null -eq (Compare-Object $first_3bytes $UTF8_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-8 BOM."
    }
    # UTF-16 BE
    elseif ($null -eq (Compare-Object $first_2bytes $UTF16BE_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-16 BE BOM."
    }
    # UTF-16 LE
    elseif ($null -eq (Compare-Object $first_2bytes $UTF16LE_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-16 LE BOM."
    }
    # UTF-32 BE
    elseif ($null -eq (Compare-Object $first_4bytes $UTF32BE_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-32 BE BOM."
    }
    # UTF-32 LE
    elseif ($null -eq (Compare-Object $first_4bytes $UTF32LE_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-32 LE BOM."
    }
    else {
        Write-Host "[$($targetfile)] is not BOM." -ForegroundColor Red
    }
}

#################################################################################
# 処理名  | Get-PyEncoding
# 機能   | Pythonでファイルの文字コードを判定
#          | 参考情報:https://zenn.dev/haretokidoki/articles/aea5b45679d966
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | $targetfile 対象ファイル
#################################################################################
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_targetfile = (Convert-Path $targetfile)
    
    # データ種類のチェック
    if (-Not(Test-Path $fullpath_targetfile -PathType Leaf)) {
        Write-Host "[$fullpath_targetfile] is not a file." -ForegroundColor Red
        return
    }

    # Pythonスクリプトのコード
    [System.String[]]$py_source = 
@"
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]$pyfolder_path = "$PROFILE\..\user-defined-py"
    if (-Not(Test-Path $pyfolder_path)) {
        New-Item -Path $pyfolder_path -ItemType 'directory' -Force > $null
    }
    $pyfolder_path = (Convert-Path $pyfolder_path)
    #   今回実行するPythonスクリプトがない場合は、新規作成
    [System.String]$pyscript_path = "$pyfolder_path\chardet_runner.py"
    if (-Not(Test-Path $pyscript_path)) {
        # なぜかLFコードのデータであるため、CRLFに変換
        $py_source = $py_source -Replace "`n", "`r`n"
        $utf8Encoding = New-Object System.Text.UTF8Encoding
        [System.IO.File]::WriteAllText($pyscript_path, $py_source, $utf8Encoding)
    }

    # Pythonスクリプトの実行
    try {
        python $pyscript_path $fullpath_targetfile
    }
    catch {
        Write-Host 'Python script execution error.'
        return
    }
}

'@ -split "`r`n"

# プロファイル用のPowerShellスクリプト($PROFILE)がない場合は、新規作成
if (-Not(Test-Path $PROFILE)) {
    # LF変換
    $header = $header -Replace "`r`n", "`n"
    # 文字コードは UTF-8 BOM付き で新規作成
    $utf8Encoding = New-Object System.Text.UTF8Encoding $true
    [System.IO.File]::WriteAllText($PROFILE, $header, $utf8Encoding)
}

# プロファイル が UTF-8 BOM付き であるか確認
$UTF8_BOM = [System.Byte[]](0xEF,0xBB,0xBF)
[System.Byte[]]$first_3bytes = (Get-Content -Path $PROFILE -Encoding Byte -TotalCount 3)
if (-Not($null -eq (Compare-Object $first_3bytes $UTF8_BOM -SyncWindow 0))) {
    Write-Host "プロファイルのPowerShellスクリプト[$PROFILE]の文字コードが「UTF-8 BOM付き」ではありません。" -ForegroundColor Red
    Write-Host "処理を中断します。" -ForegroundColor Red
    return
}

# ハードコーディングした自作Function($ps_source)をプロファイルに追記
[System.String]$name = ''
[System.Boolean]$is_skip = $false
$func_lines = $ps_source -split "`n" | Where-Object { $_ -match "^Function " }
if ($func_lines.Count -gt 0) {
    foreach ($line in $func_lines) {
        $name = ($line -split ' ' | Select-Object -First 2)
        If (Select-String -Path $PROFILE -Pattern $name) {
            Write-Host 'ハードコーディングしたコード($ps_source)のFunctionがプロファイルに定義済みです。ハードコーディングした自作Functionの追記処理はスキップします。'
            Write-Host "(プロファイルに登録済みのFunction名: [$name])"
            $is_skip = $true
            break
        }
    }
    # ハードコーディングした自作Functionを追記
    if (-Not($is_skip)) {
        # LF変換
        $ps_source = $ps_source -Replace "`r`n", "`n"
        Add-Content -Path $PROFILE -Value $ps_source
    }
}

# ps1ファイルを格納する専用フォルダーがない場合は、フォルダーを作成
[System.String]$psfolder_path = "$PROFILE\..\user-defined-ps"
if (-Not(Test-Path $psfolder_path)) {
    # フォルダーを作成
    New-Item -Path $psfolder_path -ItemType 'directory' -Force > $null
}
$psfolder_path = (Convert-Path $psfolder_path)
# 専用フォルダー内の自作Functionをプロファイルに追記
$name = ''
$is_skip = $false
# 専用フォルダー配下にps1ファイルがない場合は、この処理が最終処理となるため早期リターン
if (-Not(Test-Path "$psfolder_path\*.ps1")) {
    Write-Host "専用フォルダー [$psfolder_path] にps1ファイルがありません。処理を終了します。" 
    return
}

[System.String]$psfolder_source = (Get-Content "$psfolder_path\*.ps1" -Raw)
$func_lines = $psfolder_source -split "`n" | Where-Object { $_ -match "^Function " }
if ($func_lines.Count -gt 0) {
    foreach ($line in $func_lines) {
        $name = ($line -split ' ' | Select-Object -First 2)
        If (Select-String -Path $PROFILE -Pattern $name) {
            Write-Host '専用フォルダー内のFunctionがプロファイルに定義済みです。専用フォルダーにある自作Functionの追記処理はスキップします。。'
            Write-Host "(プロファイルに登録済みのFunction名: [$name])"
            $is_skip = $true
            break
        }
    }
    # 専用フォルダーにある自作Functionの追記
    if (-Not($is_skip)) {
        # LF変換
        $psfolder_source = $psfolder_source -Replace "`r`n", "`n"
        Add-Content -Path $PROFILE -Value $psfolder_source
    }
}

この内容をPowerShellウィンドウで実行すると、ハードコーディングした自作Functionがプロファイルに反映され、
下記の結果が返ってきます。

なお、今回は専用フォルダー配下にps1ファイルは格納せずに実行しました。実行結果からも、その旨のメッセージが表示されています。

実際の実行結果(PowerShellウィンドウの結果)
専用フォルダー [D:\Documents\WindowsPowerShell\user-defined-ps]  にps1ファイルがありません。処理を終了します。
PS C:\Users\"ユーザー名">

下記が実行後、ハードコーディングしたコードが反映されたプロファイルの中身。
なお、プロファイルの内容はリアルタイムで読み込みされません。PowerShellウィンドウを起動する時にプロファイルが読み込まれるため、プロファイルの内容を変更した場合はPowerShellウィンドウを再起動してください。

「Microsoft.PowerShell_profile.ps1」
#################################################################################
# PowerShellプロファイル
# (D:\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1)
#
#################################################################################

#################################################################################
# 処理名  | VerificationEnv
# 機能   | PowerShell環境チェック
#          | 参考情報:https://zenn.dev/haretokidoki/articles/fac8e50fbe9dcd
#--------------------------------------------------------------------------------
# 戻り値  | MESSAGECODE(enum)
# 引数   | なし
#################################################################################
# エラーコード enum設定
Add-Type -TypeDefinition @"
    public enum MESSAGECODE {
        Successful = 0,
        Abend,
        Cancel,
        Error_NotCore,
        Error_NotSupportedVersion,
        Error_NotWindows
    }
"@
Function VerificationEnv {
    [MESSAGECODE]$return_code = [MESSAGECODE]::Successful

    # 環境情報を取得
    [System.Collections.Hashtable]$ps_ver = $PSVersionTable

    # 環境の判定:Coreではない場合(5.1だと'Desktop'となる)
    if ($ps_ver.PSEdition -ne 'Core') {
        $return_code = [MESSAGECODE]::Error_NotCore
        Write-Host 'Core(6.0以降)の環境ではない' -ForegroundColor Red
    }
    # 環境の判定:メジャーバージョンが7より小さい場合
    elseif ($ps_ver.PSVersion.Major -lt 7) {
        $return_code = [MESSAGECODE]::Error_NotSupportedVersion
        Write-Host 'Core(6.0以降)の環境だが、7以上 の環境ではない' -ForegroundColor Red
    }
    # 環境の判定:Windows OSではない場合(PowerShell Coreのみ使用できる自動変数)
    elseif (-Not($IsWindows)) {
        $return_code = [MESSAGECODE]::Error_NotWindows
        Write-Host 'Core(6.0以降)の環境で、かつ 7以上 の環境だが、Windows OS の環境ではない' -ForegroundColor Red
    }
    else {
        Write-Host 'Core(6.0以降)の環境で、かつ 7以上 の環境、Windows OS の環境である'
    }

    return $return_code
}

#################################################################################
# 処理名  | Get-Datatype
# 機能   | 変数のデータ型を確認
#          | 参考情報:https://zenn.dev/haretokidoki/articles/1632e92c37ea98
#--------------------------------------------------------------------------------
# 戻り値  | データ型を表示(項目1:BaseType、項目2:Type)
# 引数   | $variable 調査対象の変数
#################################################################################
Function Get-Datatype {
    Param (
        [Parameter(Mandatory=$true)]$variable
    )

    # 文字列配列を宣言
    [System.String[]]$rowdata = @(
        $variable.GetType().BaseType.FullName,      # 変数のベースタイプ
        $variable.GetType().FullName                # 変数のデータ型
    )
    
    # PSCustomObjectで項目名を設定
    $types_table = [PSCustomObject]@{
        BaseType = $rowdata[0]
        DataType = $rowdata[1]
    }

    # コンソールで表示
    $types_table | Format-Table -Property BaseType, DataType -AutoSize -Wrap
}

#################################################################################
# 処理名  | Test-IsAdmin
# 機能   | PowerShellが管理者として実行しているか確認
#          | 参考情報:https://zenn.dev/haretokidoki/articles/67788ca9b47b27
#--------------------------------------------------------------------------------
# 戻り値  | Boolean(True: 管理者権限あり, False: 管理者権限なし)
# 引数   | -
#################################################################################
Function Test-IsAdmin {
    $win_id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $win_principal = new-object System.Security.Principal.WindowsPrincipal($win_id)
    $admin_permission = [System.Security.Principal.WindowsBuiltInRole]::Administrator
    return $win_principal.IsInRole($admin_permission)
}

#################################################################################
# 処理名  | GetPsCharcode
# 機能   | PowerShellコンソールの文字コードを取得
#          | 参考情報:https://zenn.dev/haretokidoki/articles/67788ca9b47b27
#--------------------------------------------------------------------------------
# 戻り値  | ps_charcode[]
#          |  - 項目01 文字エンコードを指定できるコマンドレットの既定値
#          |  - 項目02 PowerShellから外部プログラムに渡す文字エンコードの設定
#          |  - 項目01 PowerShellのコンソールに出力する文字エンコードの設定
# 引数   | -
#################################################################################
Function GetPsCharcode {
    [System.String[]]$ps_charcode = @()
    $ps_charcode = @(
        # 文字エンコードを指定できるコマンドレットの既定値
        ($PSDefaultParameterValues['*:Encoding']),
        # PowerShellから外部プログラムに渡す文字エンコードの設定
        ($global:OutputEncoding).WebName,
        # PowerShellのコンソールに出力する文字エンコードの設定
        ([console]::OutputEncoding).WebName
    )

    return $ps_charcode
}

#################################################################################
# 処理名  | ChangeWindowTitle
# 機能   | PowerShellウィンドウのタイトル変更(文字コードとPowerShellの管理者権限有無を追加)
#          | 参考情報:https://zenn.dev/haretokidoki/articles/67788ca9b47b27
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | -
#################################################################################
# PowerShellウィンドウのタイトル変更
Function ChangeWindowTitle {
    # 区切り文字の設定
    [System.String]$pos1 = '|'
    [System.String]$pos2 = ';'

    # 現在のタイトルを取得
    [System.String]$title = $Host.UI.RawUI.WindowTitle
    [System.String]$base_title = $title

    # 既にこのFunctionでタイトル変更している場合、一番左にある文字列を抽出
    [System.String[]]$title_array = $title.Split($pos1)
    if ($title_array.Length -ne 0) {
        $base_title = ($title_array[0]).TrimEnd()
    }

    # 現在の文字コードを取得しタイトルに追加
    [System.String[]]$ps_charcode = GetPsCharcode

    [System.String]$change_title = $base_title
    if (Test-IsAdmin) {
        # 管理者として実行している場合
        $change_title = $base_title + " $pos1 " +
                        "DefaultParameter='$($ps_charcode[0])'" + " $pos2 " +
                        "GlobalEncoding='$($ps_charcode[1])'" + " $pos2 " +
                        "ConsoleEncoding='$($ps_charcode[2])'" + " $pos2 " +
                        "#Administrator"
    }
    else {
        # 管理者として実行していない場合
        $change_title = $base_title + " $pos1 " +
                        "DefaultParameter='$($ps_charcode[0])'" + " $pos2 " +
                        "GlobalEncoding='$($ps_charcode[1])'" + " $pos2 " +
                        "ConsoleEncoding='$($ps_charcode[2])'" + " $pos2 " +
                        "#Not_Administrator"
    }
    $Host.UI.RawUI.WindowTitle = $change_title

    # 完了メッセージ
    Write-Host 'タイトルに“文字コード”と“管理者権限の有無”の情報を追加しました。' -ForegroundColor Cyan
}

#################################################################################
# 処理名  | SetPsOutputEncoding
# 機能   | PowerShellにおける複数の文字コード設定を一括変更
#          | 参考情報:https://zenn.dev/haretokidoki/articles/8946231076f129
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | $charcode(引数を省略した場合は、'reset_encoding'で設定)
#      |  - 'utf8'          : UTF-8に設定
#      |  - 'sjis'          : Shift-JISに設定
#      |  - 'ascii'         : US-ASCIIに設定
#      |  - 'rm_encoding'   : デフォルトパラーメーターを削除
#      |  - 'reset_encoding': 規定値に戻す
#################################################################################
Function SetPsOutputEncoding {
    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呼び出し
    ChangeWindowTitle
}

#################################################################################
# 処理名  | Get-PsEncoding
# 機能   | PowerShellでファイルの文字コードを判定
#          | 参考情報:https://zenn.dev/haretokidoki/articles/962a7fc6c51b47
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | $targetfile 対象ファイル
#################################################################################
Function Get-PsEncoding {
    Param (
        [Parameter(Mandatory=$true)][System.String]$targetfile
    )
    $stream_reader = [System.IO.StreamReader] $targetfile
    $profile_encoding = $stream_reader.CurrentEncoding
    $stream_reader.Close()

    Write-Host "EncodingName: [$($profile_encoding.EncodingName)]"
}

#################################################################################
# 処理名  | Check-BOMStatus
# 機能   | PowerShellでファイルの文字コードにおけるBOMを判定
#          | 参考情報:https://zenn.dev/haretokidoki/articles/962a7fc6c51b47
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | $targetfile 対象ファイル
#################################################################################
Function Check-BOMStatus {
    Param (
        [Parameter(Mandatory=$true)][System.String]$targetfile
    )
    if (-Not (Test-Path $targetfile)) {
        Write-Host '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[]]$first_4bytes = (Get-Content -Path $targetfile -Encoding Byte -TotalCount 4)
    [System.Byte[]]$first_3bytes = $first_4bytes[0..2]
    [System.Byte[]]$first_2bytes = $first_4bytes[0..1]
    
    # 先頭バイトでBOM付きか判定
    # UTF-7
    if (($null -eq (Compare-Object $first_4bytes $UTF7_BOM1 -SyncWindow 0)) -Or
        ($null -eq (Compare-Object $first_4bytes $UTF7_BOM2 -SyncWindow 0)) -Or
        ($null -eq (Compare-Object $first_4bytes $UTF7_BOM3 -SyncWindow 0)) -Or
        ($null -eq (Compare-Object $first_4bytes $UTF7_BOM4 -SyncWindow 0))) {
        Write-Host "[$($targetfile)] is UTF-7 BOM."
    }
    # UTF-8
    elseif ($null -eq (Compare-Object $first_3bytes $UTF8_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-8 BOM."
    }
    # UTF-16 BE
    elseif ($null -eq (Compare-Object $first_2bytes $UTF16BE_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-16 BE BOM."
    }
    # UTF-16 LE
    elseif ($null -eq (Compare-Object $first_2bytes $UTF16LE_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-16 LE BOM."
    }
    # UTF-32 BE
    elseif ($null -eq (Compare-Object $first_4bytes $UTF32BE_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-32 BE BOM."
    }
    # UTF-32 LE
    elseif ($null -eq (Compare-Object $first_4bytes $UTF32LE_BOM -SyncWindow 0)) {
        Write-Host "[$($targetfile)] is UTF-32 LE BOM."
    }
    else {
        Write-Host "[$($targetfile)] is not BOM." -ForegroundColor Red
    }
}

#################################################################################
# 処理名  | Get-PyEncoding
# 機能   | Pythonでファイルの文字コードを判定
#          | 参考情報:https://zenn.dev/haretokidoki/articles/aea5b45679d966
#--------------------------------------------------------------------------------
# 戻り値  | -
# 引数   | $targetfile 対象ファイル
#################################################################################
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_targetfile = (Convert-Path $targetfile)
    
    # データ種類のチェック
    if (-Not(Test-Path $fullpath_targetfile -PathType Leaf)) {
        Write-Host "[$fullpath_targetfile] is not a file." -ForegroundColor Red
        return
    }

    # Pythonスクリプトのコード
    [System.String[]]$py_source = 
@"
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]$pyfolder_path = "$PROFILE\..\user-defined-py"
    if (-Not(Test-Path $pyfolder_path)) {
        New-Item -Path $pyfolder_path -ItemType 'directory' -Force > $null
    }
    $pyfolder_path = (Convert-Path $pyfolder_path)
    #   今回実行するPythonスクリプトがない場合は、新規作成
    [System.String]$pyscript_path = "$pyfolder_path\chardet_runner.py"
    if (-Not(Test-Path $pyscript_path)) {
        $utf8Encoding = New-Object System.Text.UTF8Encoding
        [System.IO.File]::WriteAllText($pyscript_path, $py_source, $utf8Encoding)
    }

    # Pythonスクリプトの実行
    try {
        python $pyscript_path $fullpath_targetfile
    }
    catch {
        Write-Host 'Python script execution error.'
        return
    }
}

まとめ

  • 下記の2つから自作Functionを読み込み、PowerShellのプロファイルに定義できた!
    • ハードコーディングした自作Function
    • 専用フォルダー配下にあるps1ファイル内の自作Function

関連記事

https://zenn.dev/haretokidoki/articles/1632e92c37ea98
https://haretokidoki-blog.com/pasocon_powershell-startup/
https://zenn.dev/haretokidoki/articles/7e6924ff0cc960
https://zenn.dev/haretokidoki/articles/fb6830f9155de5

GitHubで編集を提案

Discussion