前回は、Linuxの環境倉数PATHに収められおいるコマンドをフルパスのリストずしお埗られるようにした。これは、Linuxの環境倉数PATHが本来持っおいる怜玢の優先順䜍を反映させるためだ。 取埗するコマンドのパスをフルパスに倉曎するこずで、どこのパスのLinuxコマンドが実行されおいるのかわかるようになった。次のスクリプトが、該圓する倉曎郚分だ。

$_linux_command_paths = (
    wsl ls -d ($_linux_path[($_linux_path.length-1)..0] -replace "$","/*")
) 2> $null

スクリプト自䜓は短いが、PowerShellを利甚する䞊で重芁ないく぀かのティップスが䜿われおいる。以䞋に、このスクリプトのポむントをもう䞀床たずめおおこう。

  1. 配列の䞭身を逆順で䜿う
  2. 配列の䞭身に察しお個々に文字列眮換を実斜
  3. 文字列眮換を正芏衚珟で指定
  4. ゚ラヌメッセヌゞを削陀
  5. 実行結果を倉数などに代入せずにそのたた利甚


今回はこのデヌタをこれたでに䜜成した$PROFILEに適甚しお、コマンド実行の優先順䜍をPowerShell偎で制埡できるように曞き換える。

フルパスを掻甚する凊理に倉曎する

たずLinuxコマンドを関数でラッピングするためのスクリプトをチェックする。これたでは次のようになっおいた。

ForEach($n in $_linux_command_names) {
    if ($n -ne "") {
        $_linux_functions += "
            function $n {
                if (`$Input.Length) {
            `$Input.Reset()
                    `$Input | wsl $n `$(_path_to_linux `$Args)
        }
        else {
                    wsl $n `$(_path_to_linux `$Args)
        }
            }"
    }
}

先に結果を掲茉しおおく。䞊蚘を次のように曞き換えるこずで、今回の目的を達成できる。

ForEach($n in $_linux_command_paths) {
    $_n = (Split-Path -Leaf $n)
    $_linux_functions += "
        function $_n {
            if (`$Input.Length) {
                `$Input.Reset()
                `$Input | wsl $n `$(_path_to_linux `$Args)
            }
            else {
                wsl $n `$(_path_to_linux `$Args)
            }
        }"
}
Remove-Variable n
Remove-Variable _n

では、现かく芋おいこう。たず、これたでコマンド名だけでルヌプを回しおいた郚分を、次のようにフルパスのコマンドリストで回すように倉曎する。これで倉数$nにはフルパスのコマンドが代入されおいるこずになる。

ForEach($n in $_linux_command_paths) {

ただし、関数名はフルパスではなくコマンド名で䜜成するので、フルパスからコマンド名を取り出す必芁がある。その凊理を行っおいるのが次の凊理だ。

    $_n = (Split-Path -Leaf $n)

この凊理で倉数$_nにコマンド名ファむル名が入るこずになる。コマンド名だけを取り出す方法はほかにもあり、文字列の加工凊理などを䜿っおもよいだろう。ここでは、蚘述が簡単な䞊蚘の凊理を䜿った。

関数を定矩しおいる郚分が、次のように曞き換わるこずになる。

        function $_n {

以䞋の2぀の行に関しおは、蚘述自䜓はこれたでず同じだ。

`$Input | wsl $n `$(_path_to_linux `$Args)
 wsl $n `$(_path_to_linux `$Args)

ただし、倉数$nにはコマンド名ではなくフルパスが入っおいる。぀たり、wslにフルパスを指定しおコマンドを実行する凊理に倉わっおいるこずになる。これで曞き換え完了だ。

次の2行はおたけだ。䞀応、ここだけでしか䜿っおいない倉数なので、必芁性がなくなった時点で倉数を削陀しおいる。

Remove-Variable n
Remove-Variable _n

動䜜を確認しおみよう

曞き換えた$PROFILEでPowerShellを起動しお動䜜を確かめおみる。$_linux_command_pathsず$_linux_functionsは消さずに残しおあるので、その䞭身を確認しおみよう。たず、$_linux_command_pathsを確認するず、次のようになっおいる。

$_linux_command_pathsの䞭身を確認

優先順䜍の䜎いパスのコマンドが最初に衚瀺され、優先順䜍の高いパスが最埌に衚瀺されおいるこずがわかる。ここたでは前回ず同じだ。次に$_linux_functionsの䞭身を確認するず、次のようになっおいるこずがわかる。

$_linux_functionsの䞭身を確認

wslで指定しおいるコマンドがこれたでのコマンド名だけからフルパスに倉わっおいるこずがわかる。実際には倉曎する前ず埌で実際に実行されるコマンドに違いはないのだが、今回の曞き倉えでPowerShell偎から実行するコマンドを制埡しやすい状態にはなった。PowerShellの動䜜䟋ずしおも、簡単なわりに効果が芋蟌みやすいものだず蚀えるだろう。

これたでに䜜っおきた$PROFILEによっお、このPowerShellはLinux偎のコマンドをかなりシヌムレスに実行できるようになった。普段LinuxやmacOSのタヌミナルで䜜業をしおいる方であれば、この環境は結構なじみやすいのではないかず思う。ただいく぀かの点で䞍䟿なずころが残っおいるので、そのあたりをチュヌニングしおいけば、PowerShellずWindows偎のコマンド、そしおLinuxのコマンドをさらにシヌムレスに実行できるようになるはずだ。

付録: $PROFILE

本連茉時点での$PROFILEを次に掲茉しおおく。

# Copyright (c) 2020 Daichi GOTO <daichi@ongs.co.jp>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.

# author: Daichi GOTO (daichi@ongs.co.jp)
# first edition: Mon Jun 22 18:20:36 JST 2020

#========================================================================
# Definition of Linux commands used via wsl
#========================================================================
$_linux_path = (wsl echo '$PATH').Split(":") -NotMatch "/mnt"
$_linux_command_paths = (
    wsl ls -d ($_linux_path[($_linux_path.length-1)..0] -replace "$","/*")
) 2> $null

# Generate Linux command functions
ForEach($n in $_linux_command_paths) {
    $_n = (Split-Path -Leaf $n)
    $_linux_functions += "
        function $_n {
            if (`$Input.Length) {
                `$Input.Reset()
                `$Input | wsl $n `$(_path_to_linux `$Args)
            }
            else {
                wsl $n `$(_path_to_linux `$Args)
            }
        }"
}
Remove-Variable n
Remove-Variable _n

$_linux_functions += @'
    function _path_to_linux {
        $linuxpath = @()

        # Convert arguments to Linux path style
        ForEach($winpath in $Args) {
            if ($winpath -eq $null) {
                Break
            }

            # Change drive path to mount path
            if ($winpath -match '^[A-Z]:') {
                $drive = $winpath.Substring(0,1).ToLower()
                $linuxpath += "/mnt/" + $drive + $winpath.Substring(2).Replace('\','/')
            }
            # Option is not converted
            elseif ($winpath -match '^[-+]') {
                $linuxpath += $winpath
            }
            # Other argument is converted
            else {
                $linuxpath += ([String]$winpath).Replace('\','/')
            }
        }

        $linuxpath
    }
'@

# Prepare temporary file path with extension .ps1
$_temp = New-TemporaryFile
$_temp_ps1 = $_temp.FullName + ".ps1"
Remove-Item $_temp

# Write function definition to temporary .ps1 file and parse
$_linux_functions | Out-File $_temp_ps1
. $_temp_ps1
Remove-Item $_temp_ps1

# Delete unnecessary variables
Remove-Variable _temp
Remove-Variable _temp_ps1
#Remove-Variable _linux_path
#Remove-Variable _linux_command_paths
#Remove-Variable _linux_functions

#========================================================================
# Individual Linux command function definitions
#========================================================================
# grep
function grep {
    $pattern_exists = $False
    $path_exists = $False
    $skip = $False
    $i = 0

    ForEach($a in $Args) {
        if ($skip) {
            $skip = $False
            $i++
            continue
        }

        # Options without argumetn
        if ($a -cmatch '^-[abcdDEFGHhIiJLlmnOopqRSsUVvwxZ]') {
        }
        # Options with argument
        elseif ($a -cmatch '^-[ABC]') {
            $skip = $True
        }
        # Pattern file specification option
        elseif ($a -ceq '-f') {
            $skip = $True
            $pattern_exists = $True
            $Args[$i+1] = _path_to_linux $Args[$i+1]
        }
        # Pattern specification option
        elseif ($a -ceq '-e') {
            $skip = $True
            $pattern_exists = $True
        }
        # Pattern or file path
        elseif ($a -cnotmatch '^-') {
            if ($pattern_exists) {
                $path_exists = $True
            }
            else {
                $pattern_exists = $True
            }
        }

        $i++
    }

    # Change file path
    if ($path_exists) {
        $Args[-1] = _path_to_linux $Args[-1]
    }

    $Input | wsl grep $Args
}

# ls
Get-Alias ls *> $null && Remove-Item alias:ls
function ls { wsl ls --color=auto $Args }
function ll { ls -l }
function la { ls -a }

#========================================================================
# Alias definition
#========================================================================
Set-Alias -Name open -Value explorer
Set-Alias -Name edge -Value "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
Set-Alias -Name chrome -Value "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"