マイナビニュースマイナビ

PowerShell 7をシェルスクリプトとして使う - パイプラインを使う(その2)

【連載】

PowerShell Core入門 - 基本コマンドの使い方

【第154回】PowerShell 7をシェルスクリプトとして使う - パイプラインを使う(その2)

[2021/06/25 08:00]後藤大地 ブックマーク ブックマーク

指定されたパスがディレクトリパスであるかどうかを判定するスクリプトの作成は、今回で最後だ。ごくシンプルな処理を実行するスクリプトだが、だいぶそれらしい体裁になった。それほど厳密なスクリプトではないが、スクリプトに必要な最低限の要素はだいたい網羅したのではないかと思う。

パイプラインに対応させる - ファイナル

前回の処理でディレクトリの指定を引数からではなく、パイプ経由からも取得できるようにした。成果物(test-d_15.ps1)は次のようになった。

#!/usr/bin/env pwsh

<# 
.SYNOPSIS
ディレクトリパスが存在するかどうかを判定。

.DESCRIPTION
指定されたパスがディレクトリパスであり、かつ、存在するかどうかを判定する。

.PARAMETER DirectoryPath
ディレクトリパスを指定。

.PARAMETER Help
ヘルプメッセージを表示。

.INPUTS
ディレクトリパス。引数でディレクトリパスが指定されていない場合に使われる。

.OUTPUTS
System.Boolean。ディレクトリパスが存在する場合にTrue、そうでない場合にFalse。

.EXAMPLE
PS> .\test-d .\Documents\
True

.EXAMPLE
PS> .\test-d .\Documents-non-exists\
False

.EXAMPLE
PS> dir | .\test-d
True

.LINK
Test-Path
#>

#====================================================================
# 引数
#====================================================================
Param(
    [string]$DirectoryPath,  # ディレクトリを指定
    [switch]$Help          # ヘルプを表示
)

if ($Help) {
    Get-Help $PSCommandPath
    exit 1
}
# 引数でディレクトリが指定されていない場合、パイプからディレクトリ
# パスの取得を試みる。
if (-Not $DirectoryPath) {
    $DirectoryPath = @($input)[0]

    if (-Not $DirectoryPath) {
        Get-Help $PSCommandPath
        exit 1
    }
}

#====================================================================
# ディレクトリパスの存在確認
#====================================================================
if (Test-Path -PathType Container -Path $DirectoryPath) {
    # 指定されたパスはディレクトリであり、存在している。
    $True
} else {
    # 指定されたパスはディレクトリではない。
    if (Test-Path -PathType Leaf -Path $DirectoryPath) {
        # 指定されたパスはファイル。
        $infomsg = "$DirectoryPath : " `
            + "ファイルです。"
    } else {
        # 指定されたパスは存在しない。
        $infomsg = "$DirectoryPath : " `
            + "そのようなディレクトリは存在しません。"
    }
    Write-Host $infomsg
    $False
}

パイプで渡ってくるデータは必ずしもパスが1つだけとは限らない。「dir」の出力が渡ってきたときなどは複数のパスが流れてくることになる。前回はパイプからの入力を「@($input)[0]」のようにアクセスすることで強制的に最初の1つに絞っていたわけだが、今回はパイプで流れてくる全てのデータを処理するように書き換えていく。

パイプから複数のパス指定を取るバージョン

というわけで、最初に答えからだ。以下のように書き換えることで、パイプから渡されてくるデータをすべてパスとして判定処理することができる(test-d_16.ps1)。

#!/usr/bin/env pwsh

<# 
.SYNOPSIS
ディレクトリパスが存在するかどうかを判定。

.DESCRIPTION
指定されたパスがディレクトリパスであり、かつ、存在するかどうかを判定する。

.PARAMETER DirectoryPath
ディレクトリパスを指定。

.PARAMETER Help
ヘルプメッセージを表示。

.INPUTS
ディレクトリパス。引数でディレクトリパスが指定されていない場合に使われる。

.OUTPUTS
System.Boolean。ディレクトリパスが存在する場合にTrue、そうでない場合にFalse。

.EXAMPLE
PS> .\test-d .\Documents\
True

.EXAMPLE
PS> .\test-d .\Documents-non-exists\
False

.EXAMPLE
PS> dir | .\test-d
C:\Users\daichi\dir: True
C:\Users\daichi\file: False

.LINK
Test-Path
#>

#====================================================================
# 引数
#====================================================================
Param(
    [string]$DirectoryPath,  # ディレクトリを指定
    [switch]$Help          # ヘルプを表示
)

if ($Help) {
    Get-Help $PSCommandPath
    exit 1
}

#====================================================================
# 引数でディレクトリが指定されていない場合、パイプからディレクトリ
# パスの取得を試みつつ、ディレクトリパスの存在確認
#====================================================================
if (-Not $DirectoryPath) {
    $untreated = $True

    foreach ($DirectoryPath in $input) {
        $untreated = $False

        if (Test-Path -PathType Container -Path $DirectoryPath) {
            # 指定されたパスはディレクトリであり、存在している。
            $DirectoryPath + ": " + $True
        } else {
            # 指定されたパスはディレクトリではない。
            if (Test-Path -PathType Leaf -Path $DirectoryPath) {
                # 指定されたパスはファイル。
                $DirectoryPath + ": " + $False
            } else {
                # 指定されたパスは存在しない。
                $DirectoryPath + ": no such file or directory."
            }
        }
    }

    if ($untreated) {
        Get-Help $PSCommandPath
    }

    exit 1
}

#====================================================================
# ディレクトリパスの存在確認
#====================================================================
if (Test-Path -PathType Container -Path $DirectoryPath) {
    # 指定されたパスはディレクトリであり、存在している。
    $True
} else {
    # 指定されたパスはディレクトリではない。
    if (Test-Path -PathType Leaf -Path $DirectoryPath) {
        # 指定されたパスはファイル。
        $infomsg = "$DirectoryPath : " `
            + "ファイルです。"
    } else {
        # 指定されたパスは存在しない。
        $infomsg = "$DirectoryPath : " `
            + "そのようなディレクトリは存在しません。"
    }
    Write-Host $infomsg
    $False
}

具体的に、どの部分を書き換えたかを説明しよう。

変更した部分 - パイプ処理

まず、前回のスクリプトではパイプから渡されるデータを「@($input)[0]」でアクセスしていた。これだと最初の1個が強制的に処理の対象になる。該当部分は次のコードだ。

# 引数でディレクトリが指定されていない場合、パイプからディレクトリ
# パスの取得を試みる。
if (-Not $DirectoryPath) {
    $DirectoryPath = @($input)[0]

    if (-Not $DirectoryPath) {
        Get-Help $PSCommandPath
        exit 1
    }
}

まず、この部分を「@($input)[0]」のところ以外は意味が変わらないように、次のような感じに書き替える。

大枠の書き換え

#====================================================================
# 引数でディレクトリが指定されていない場合、パイプからディレクトリ
# パスの取得を試みつつ、ディレクトリパスの存在確認
#====================================================================
if (-Not $DirectoryPath) {
    $untreated = $True

// ここに新しい処理

    if ($untreated) {
        Get-Help $PSCommandPath
    }

    exit 1
}

これでパイプから何もデータが流れてこなかった場合にはヘルプが表示されるようになる。前回の処理を踏襲したものだ。

次に、パイプからデータを読み取ってそれぞれ処理するコードを追加する。ポイントは「foreach ($DirectoryPath in $input) { … }」のようにしてデータを個別に判定処理するようにしたところだ。次のように書き換えてある。

#====================================================================
# 引数でディレクトリが指定されていない場合、パイプからディレクトリ
# パスの取得を試みつつ、ディレクトリパスの存在確認
#====================================================================
if (-Not $DirectoryPath) {
    $untreated = $True

    foreach ($DirectoryPath in $input) {
        $untreated = $False

        if (Test-Path -PathType Container -Path $DirectoryPath) {
            # 指定されたパスはディレクトリであり、存在している。
            $DirectoryPath + ": " + $True
        } else {
            # 指定されたパスはディレクトリではない。
            if (Test-Path -PathType Leaf -Path $DirectoryPath) {
                # 指定されたパスはファイル。
                $DirectoryPath + ": " + $False
            } else {
                # 指定されたパスは存在しない。
                $DirectoryPath + ": no such file or directory."
            }
        }
    }

    if ($untreated) {
        Get-Help $PSCommandPath
    }

    exit 1
}

実行すると次のようになる。

パイプから複数のパスを取得して処理 その1

パイプから複数のパスを取得して処理 その2

このスクリプトの機能としては、ここまで仕上げれば十分だろう。

ヘルプをアップデート

パイプ経由でデータが来たときの挙動が変わったので、ヘルプの内容を書き換える。次の部分が前回のヘルプの内容だ。

.EXAMPLE
PS> dir | .\test-d
True

これを次のように書き換える。

.EXAMPLE
PS> dir | .\test-d
C:\Users\daichi\dir: True
C:\Users\daichi\file: False

実行すると、次のようにヘルプのサンプルが更新されたことを確認できる。

更新されたヘルプを確認

最初のスクリプトはこれでコンプリート

本連載では数回にわたり、ごく簡単な機能をピックアップしてPowerShellスクリプトに書き換えていく方法を取り上げてきた。スクリプトは用途に応じていくつも作成していくものだが、ここで取り上げたやり方は、ほかの多くのスクリプトへ応用することができる。

スクリプトのコツは、あまり詰め込まないこと、こだわらないこと、必要に応じて新しく作成したり、すぐに書き換えたりできるようにしておくことだ。あくまでも自分が使うツールとして、手軽にいつでも作れる――そういう位置付けで活用していきたい。

今後は、作業を自動化することで効率を高めることが重要になるのは言うまでもない。次回以降は、より実用的な機能を取り上げながらスクリプトを書く方法を紹介していく。

※ 本記事は掲載時点の情報であり、最新のものとは異なる場合がございます。予めご了承ください。

一覧はこちら

連載目次

もっと知りたい!こちらもオススメ

【連載】RPA入門 - ツールで学ぶ活用シーン

【連載】RPA入門 - ツールで学ぶ活用シーン

AIには、ルールベース、機械学習、深層学習(ディープラーニング)の3つのレベルがあり、レベルが上がるに連れてより高度な人工知能を実現しますが、AIのスピンオフという位置付けで、Digital Labor(仮想知的労働者)によるホワイトカラー業務の自動化を実現するRPAが注目されています。

関連リンク

この記事に興味を持ったら"いいね!"を Click
Facebook で TECH+ の人気記事をお届けします
注目の特集/連載
[解説動画] Googleアナリティクス分析&活用講座 - Webサイト改善の正しい考え方
Slackで始める新しいオフィス様式
Google Workspaceをビジネスで活用する
ニューノーマル時代のオウンドメディア戦略
ミッションステートメント
次世代YouTubeクリエイターの成長戦略
IoTでできることを見つけるための発想トレーニング
教えてカナコさん! これならわかるAI入門
AWSではじめる機械学習 ~サービスを知り、実装を学ぶ~
Kubernetes入門
SAFeでつくる「DXに強い組織」~企業の課題を解決する13のアプローチ~
マイクロサービス時代に活きるフレームワーク Spring WebFlux入門
AWSで作るマイクロサービス
マイナビニュース スペシャルセミナー 講演レポート/当日講演資料 まとめ
セキュリティアワード特設ページ

一覧はこちら

今注目のIT用語の意味を事典でチェック!

一覧はこちら

会員登録(無料)

ページの先頭に戻る