Powershellで特定のフォルダの拡張子の一覧を取得する

特定のフォルダ配下に含まれるファイルの拡張子の一覧を取得するための方法のメモ

Powershellを使ってファイルの拡張子を得るにはいくつか方法があって、例えばSystem.IO.Pathクラスを使う手もあったりするが、それよりはSystem.IO.FileInfoクラスを生成してしまった方が楽だし応用が効くように思う。

PS D:\> Get-ChildItem -Recurse -File | %{$_.Extension} | Sort-Object -CaseSensitive | Get-Unique
.log
.txt

やっていることは以下のような流れ

  • Get-ChildItem -Recurse -Fileでカレントディレクトリのファイルをサブディレクトリ含めて取得
  • FileInfoのExtentionフィールドで拡張子を取得して
  • Sort-Object -CaseSensitiveで大文字小文字を区別してソートしたうえで
  • Get-Uniqueでユニークにする(重複を除く)

なお、Get-ChileItemのFileパラメータはv3からしか使えないので、v2の場合はWhere-Objectの抽出条件でFileInfoのAttributeか、PSIsContainerを使って絞りこむことで代替する。

PS D:\SandBox> Get-ChildItem -Recurse | Where-Object{$_.Attribute -ne "Directory" } | %{$_.Extension} | Sort-Object -CaseSensitive | Get-Unique

PS D:\SandBox> Get-ChildItem -Recurse | Where-Object{$_.PSIsContainer -eq $false } | %{$_.Extension} | Sort-Object -CaseSensitive | Get-Unique

また、特定のファイルの拡張子を取得するときはGet-ItemでFileInfoのオブジェクトを取得すればよい。Alias(gi)を使うと楽に書ける。

PS D:\> $(Get-Item .\random.log).Extension
.log

Powershellスクリプト・モジュールのヘルプトピックの作り方

Powershellスクリプト・モジュールで正しくGet-Helpで読むことができるヘルプトピック(Help topic)を作る方法のメモ。

そもそもヘルプトピックとは?

要するにGet-Helpしたときに表示されるヘルプのこと*1。細かいことは詳しいヘルプ情報の取得 | Microsoft Docsを参照。

例えばGet-HelpコマンドレットをGet-Helpすると以下のようになる

PS D:\SandBox> get-help Get-ChildItem

名前
    Get-ChildItem

概要
    Gets the items and child items in one or more specified locations.

(以下略)

上記のようなヘルプトピックは、簡単にPowershellで自作したスクリプト・モジュールでも表示させることができるので、以降はその方法・記法を記載する*2

スクリプト(.ps1)

スクリプトの場合は、コメントベースヘルプ(Comment_Based_Help)をスクリプト内に記述することで、ヘルプトピックを作成できる。 記載場所は関数の宣言のすぐ上かすぐ下で、以下のようなイメージ。

#関数の前に書く場合
  <#
  .SYNOPSIS
   関数の概要
  .DESCRIPTION
   関数の詳細な説明
  .EXAMPLE
   関数の利用例
  .EXAMPLE
   利用例は複数記載できる
  .PARAMETER foo
  パラメータの説明
  .PARAMETER bar
  パラメータの分だけ記載する
  #>
function Do-Hoge {
   (略)
}

関数の中に書く場合
function Do-Fuga {
  <#
  .SYNOPSIS
   関数の概要
  .DESCRIPTION
   関数の詳細な説明
  .EXAMPLE
   関数の利用例
  .EXAMPLE
   利用例は複数記載できる
  .PARAMETER foo
  パラメータの説明
  .PARAMETER bar
  パラメータの分だけ記載する
  #>
 (略)
}

もっと詳しいことはGet-Help about_Comment_Based_Helpを実行するか、以下のWebページを読むとわかると思う。

モジュール(.psm1)

モジュールの場合は、コメントベースヘルプも使えるが、それ以外にもヘルプを外部ファイルに記述して読み込ませることができる。 ファイルには、モジュール全体に対する説明である「概念説明のヘルプ(Conceptual ("About") Help)」と、個々の関数について記載する「コマンドレット ヘルプファイル(Cmdlet Help File)」の2種類がある。

なお、モジュールに関しては以下のブログ記事がとても参考になる。モジュールの自動読み込みについてとか。

モジュールヘルプの場所

以下のような形でヘルプファイルを配置すると、OSの言語によって自動的に読み込むヘルプファイルを切り替えてくれる。

<ModulePath>
         \SampleModule
               \<en-US>
                     \about_SampleModule.help.txt
                     \SampleModule.extention-help.xml
                     \SampleNestedModule.extention-help.xml
               \<ja>
                     \about_SampleModule.help.txt
                     \SampleModule.extention-help.xml
                     \SampleNestedModule.extention-help.xml

このように多言語対応できるところが、この方式の強みだと思う。逆に言うと、Powershellでモジュールを書いて、かつドメスティックに使うだけならComment Basedで十分な気がする。

ロケール(en-USとか)の一覧はロケール ID (LCID) の一覧VBScriptのレファレンスだが、参考になる。

概念説明のヘルプ(Conceptual ("About") Help)

モジュール全体の説明のことで、Writing Help for Windows PowerShell Modules.aspx)に詳しい。 以下のようなファイル(内容は上記サイトからの引用)を「about_hogehoge.help.txt」という名前で作ると、Get-Help about_hogehogeで参照できる。

TOPIC
    about_<subject or module name>

SHORT DESCRIPTION
    A short, one-line description of the topic contents.

LONG DESCRIPTION
    A detailed, full description of the subject or purpose of the module.

EXAMPLES
    Examples of how to use the module or how the subject feature works in practice.

KEYWORDS
    Terms or titles on which you might expect your users to search for the information in this topic.

SEE ALSO
    Text-only references for further reading. Hyperlinks cannot work in the Windows PowerShell console. 

コマンドレット ヘルプファイル(Cmdlet Help File)

モジュールの中のメンバー(関数など)のヘルプをxmlファイル(SampleModule.extention-help.xml)に記載する。 例としては以下のような感じ(MSのサイトからの引用)。

<command:command
  xmlns:maml="http://schemas.microsoft.com/maml/2004/10"
  xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" 
  xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
  <command:details>
    <!--Add name an synopsis here-->
  </command:details>
  <maml:description>
    <!--Add detailed description here-->
  </maml:description>
  <command:syntax>
    <!--Add syntax information here-->
  </command:syntax>
  <command:parameters>
    <!--Add parameter information here-->
  </command:parameters>
  <command:inputTypes>
    <!--Add input type information here-->
  </command:inputTypes>
  <command:returnValues>
    <!--Add return value information here-->
  </command:returnValues>
  <maml:alertSet>
    <!--Add Note information here-->
  </maml:alertSet>
  <command:examples>
    <!--Add cmdlet examples here-->
  </command:examples>
  <maml:relatedLinks>
    <!--Add links to related content here-->
  </maml:relatedLinks>
</command:command>

とりあえず、参考になりそうなのは以下のサイト。よめば雰囲気でだいたいわかる。特に最後のSAPIEN社の公式ブログの「What do I name the XML help file?」はファイル命名規則について詳しく参考になる。

しかし、多言語対応するにはこのファイルを作ることが必要なのだが、残念なことにコマンドが自動でやってくれていた補完が効かない。 例えばParameterのattributeなど、コードで明示していることも、いちいち自分で書かないといけない。生のxmlを、しかも英語のレファレンスを見ながら書かざるを得ず、とてもつらい。

Git-Hubを見るとコードからヘルプ生成を自動化するプロジェクトも散見されるので、そのうち使って記事にしたいと思う。たとえばPowerShell/platyPSとか。

まとめ

ちゃんとGet-Help対応のヘルプ(Help Topic)を書こう。

*1:Linuxでいうmanコマンドで表示されるmanページのこと

*2:Linuxで例えるとmanのためのgroffマクロの書き方のようなイメージ。manページを書いたことはないけれど

VS Codeでgitを使うために認証情報を入力する

VS Codeでうまくgitが使えず、メッセージどこかに出てないかなーと思ったら、ふつうに出力タブが下にあってエラーメッセージに解決内容まで載っていた。

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

要するに認証を通せと言っているので、素直にターミナルでコマンド実行すると解消する。

参考

Visual Studio Code で Git を 使う | 験なきものを思はずは

PowershellでExcelファイルのシート名の一覧を取得したい(作りかけ2)

PowershellでExcelファイルのシート名の一覧を取得したい(作りかけ) - mk_55's diaryの続き。

とりあえず使えるようにはなった。あとはPesterでテスト書いたりする必要あり。いまだにVS Codeでgitが上手く使えない...

Excel.psm1

function Get-WorkSheetNames {
    [CmdletBinding()] #Write As a ScriptCmndlet
    Param (
        [parameter(mandatory = $true, ValueFromPipeline = $true)]
        [string[]]$Path,

        # todo need more better naming
        [parameter(mandatory = $false)]
        [boolean]$needsOutputFileName = $false
    )

    begin {
        # open Excel
        $excel = New-Object -ComObject Excel.Application
        $excel.Visible = $false
        $excel.DisplayAlerts = $false

    }

    process {
        try {
            # Argument Validation
            if (-not(Test-Path $Path -Include "*.xlsx", "*.xls", "*.xslm" )) {
                Write-Debug "File not Found or File not ExcelBook"
                throw  New-Object "System.ArgumentException" @("File not Found or not ExcelBook", $Path) -ErrorAction Stop
            }

            # Get absolute path (for Excel.Application.Workbooks.Open())
            $ExcelPath = Convert-Path $Path
            
            # Get sheet name
            $book = $excel.Workbooks.Open($ExcelPath)
            if ($needsOutputFileName) {
                $fileName = Split-Path $ExcelPath -Leaf
                $book.WorkSheets | ForEach-Object {Write-Host $filename ":" $_.Name }
            } else {
                $book.WorkSheets | ForEach-Object { $_.Name }
            }
            
        
        } catch [System.ArgumentException] {
            Write-Error $_

        } catch {
            # in order to close Excel in End Block
            Write-Errot $_
        
        } finally {
            # close book
            if ($book) {
                [void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($book)
            }
        }
    }

    end {
        # close Excel
        Write-Debug "Close Excel"
        [void]$excel.Quit()
        [void][System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
        
    }
}

Export-ModuleMember -Function Get-WorkSheetNames

PowershellでExcelファイルのシート名の一覧を取得したい(作りかけ)

最近技術的なことができてない。 なにか書こう、ということでPowershellのモジュール(作りかけだが)。

本当はgitとかにいれるべきだが、VSCodeとgitをまだ使いこなせていない感があり、ひとまず成果を上げる

Excel.psm1

function Get-WorkSheetNames {
    Param (
        [parameter(mandatory = $true)]
        [string]$excelPath
    )

    begin {
        # open Excel
        $excel = New-Object -ComObject Excel.Application
        $excel.Visible = $false
        $excel.DisplayAlerts = $false

    }
    process {
        #todo check Param
        #todo Convert Relative Path to absolute Path

        $book = $excel.Workbooks.Open($excelPath)
        $book.WorkSheets | ForEach-Object { $_.Name }

    }

    end {
        # todo kill some objects cleary  
        # close Excel
        [void]$excel.Quit()
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($book)
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
        
    }
}

Export-ModuleMember -Function Get-WorkSheetNames

音声入力をためしてみる

勝間和代氏が音声入力でブログを書いていており、音声入力による生産性の向上は、ざっくり「3倍」くらいのイメージと書いているのを見て、自分でも音声入力を試してみることにした。 この記事は音声入力で書かれている。音声入力は Google 音声入力を使い、PCとAndroidで試した。

PC の場合

Google ドキュメントを使って音声入力でテキストを入力することができる。Google ドキュメントで[ツール]⇒[音声入力]と選択すると、画面上にマイクのボタンが表示される。このボタンをクリックして赤くなっている間はマイクから音声が Google ドキュメントに自動で入力される。またショートカット(Ctrl-Shift+S)を利用することで入力のON/OFFを切り替えることができる。

Android の場合

Android の場合は Google 日本語入力を利用して音声入力をすることができる。音声入力はデフォルトでは無効になっているので Google ドキュメントの設定から有効化が必要だ。 参考:文字入力時に音声入力が行えない | ファーウェイお客様サポート

やってみて

PC とアンドロイドを比較すると以下のようなメリットデメリットがある。

PC の場合非常にスムーズに変換してくれる。ある程度の長さで入力すれば誤変換も文章単位で前後の文脈から認識してくれるようだ。また、入力しながらキーボードで入力することもでき、単純に音声入力を利用するという意味では PC の方が優れているだろう。 一方で PC の場合は 、PC の前で入力することになるが 、Android の場合は自由な場所で入力できることがメリットだ。例えば、寝ながら、家事をしながら、トイレの中で、など場所を選ばず入力できることは非常に強いメリットである。

このことから音声入力を利用したドキュメントは Google ドキュメント上に作成し、ある程度気合を入れて編集するときは PC で、ながら手入力する時はスマホでといったように、いつでもどこでも音声入力で編集できる環境を作ることが重要そうだ。

ただ、私はクラウド上のデータを OneNote や One Drive に集約している、ためどうにかどちら側に集約し、一元管理したい。OneNote にもアドインによって、音声入力は可能なのだが、Googleに比べると固有名詞の変換精度が大きく劣るので難しいところだ。

ともあれ、使い慣れたキーボードとは違った方法なので戸惑いもあるものの、喋ったことをそのまま記録してくれる、つまり口述筆記のようなことを簡単に行うことができた。非常に優秀なアウトプットの方法の一つであることは間違いない。 キーボードを使った入力と比較してみると、キーボードは非常に高速な一方で、入力の敷居が高く集中力も要する。一方の音声入力であれば、思ったことを簡単にメモしておき、後でそれを整形すればそれなりの文章を作ることも可能だ。これからは音声入力を積極的にシーンに応じて利用することが生産性を高めることにつながるだろう*1

*1:たとえば普段書くような技術的な記事は音声入力は難しくても、小説や漫画の感想を書く分には音声入力のほうがよほど優れているだろう

Windowsのアカウント名とユーザー名の関係がよく分からない

帰省したら実家のパソコンの問題を相談されるのは、割と業界あるあるだと思う。 結局問題を根本から解決できなかったのだが、事象の記録のためにメモ。

事象

Windows10のPCにエクスプローラーからファイル共有できない。 ユーザー名とパスワードが誤っている旨のメッセージが出力されるが、接続元のPCにはその情報でログインできる。 しかしnet useで接続しようとすると、システムエラー86がでるので、認証周りの問題であることは確か。

原因

ログインで使っているアカウント名と、実際にプログラムを動かしているユーザ名が不整合を起こしている

ログインしたアカウント名でパッと見は動いているように見えるのだが*1、タスクマネージャーでログインユーザーを確認すると名前がアカウント名が異なる net userを実行すると、ログインのアカウント名は表示されず、タスクマネージャーで出るユーザー名が表示される。

タスクマネージャーで表示されたユーザー名でファイル共有すると問題なく接続できた

その他

おそらくアカウント名を途中で変更した結果、不整合が起こっているのだとは思うが、そこまで手を入れると色々と面倒なので断念 ※フォルダ名やレジストリを整合性を保ちながら変更すればよいのだと思うが、他のソフトへの影響もよく分からないので運用対処とした とりあえず、ユーザーサポートに休み明けに連絡するか、タスクマネージャーで表示されたユーザー名でファイル共有するように言っておいたが......

参考

http://tooljp.com/windows/doc/net-use/net-use-error.html

*1:GUIの設定からアカウント名を確認すると、ログイン時のアカウント名が表示される