パワーシェルでファイル名の一部を取得する

今回はパワーシェルでファイル名の一部を取得(切り出し)して、変数に格納する
##構成環境
 - Windows10
 - PowerShell 5.1


例えば以下のようなファイル構成とするとき

```PowerShell
PS C:\user\xyz>Get-ChildItem -Name
1101agalaga.xyz
1102gkjghah.xyz
1103gglaglafdg.xyz
1104yagahgh.xyz
1105xb1agal.xyz
1101~1105.log
example.sys
Rename.ps1
SubString.ps1
```

上記xyzフォルダ内の＊.xyzファイルの1文字目から4文字目を取得(さらにどのxyzファイルかを指定)
特定のファイル一覧の中で一貫性のある日付部分の情報を得るなど

##特定のファイル一覧の中で任意の文字列に一致するものを取得

```powershell:SubString.ps1
$path="C:\user\xyz"
#拡張子を除くファイル名を取得
$file=(Get-ChildItem $path\*xyz).BaseName

#1文字目から4文字目を変数に格納
foreach($list in $file.SubString(0,3)){
    #1101と1102を取得する場合
    if($list-eq"1101"){
        $first=$list
    }
    if($list-eq"1102"){
        $second=$list
    }
}
Write-Host "first="$first
Write-Host "second="$second
```
実行例

```PowerShell
first= 1101
second= 1102
```

...あれ、よく見たらこれ任意の文字列をただ出力しただけであんまり意味がないコードでした

##ファイル名の任意の文字列の最初と最後を取得したいとき
以下のようにすると良いかも

```powershell:SubString.ps1
###続き$path=C:\user\xyz,$file=(Get-ChildItem $path\*xyz).BaseName
#xyzファイルの個数を取得
$total_xyz=(Get-ChildItem $path\*xyz | Measure-Object).Count
$counter=0
##1文字目から4文字目を変数に格納
foreach($list in $file.SubString(0,3)){
    #最初と最後を変数に格納(欲しい文字列が不明なときなど)
    if($counter-eq"0"){
        $first=$list
    }
    $counter+=1
    if($counter-eq$total_xyz){
        $last=$list
    }
}
Write-Host "first="$first
Write-Host "last="$last
```
最初と最後はカウンターで判定してみた
実行例

```PowerShell
first= 1101
last= 1105
```

普通にもっと良い方法がある気がする
とりあえずこれでやりたいことはできた
　　

##※ファイル名の任意の文字列の最初と最後を取得する最適解

ファイルの拡張子を除くファイル名に対して、前方の数値に一致する箇所が正規表現で一致したとき配列[0]に格納して、配列[0]の最初と最後を取得したものをパイプでつないで`Get-ChildItem`すると...
なんと一行でできるみたい

```PowerShell
Get-ChildItem  C:\User\xyz | Foreach-Object { if ($_.BaseName -match "^\d+") { $Matches[0] } } | Select-Object -First 1 -Last 1
```

##結論

:ballot_box_with_check:普通にもっと良い方法があった

##その他の文字列の最初と最後の取得パターン

 - 上記を参考にファイル名の最後尾の文字列の最初と最後の要素を取得

```powershell:SubString.ps1
###続き$path=C:\user\xyz
#ファイル名の最後の要素を取得
$get_childitem_match=Get-ChildItem  $path\*xyz | Foreach-Object { if ($_.BaseName -match ".$") { $Matches[0] } }
#取得した要素の最初と最後を変数に格納
$Last_First=$get_childitem_match | Select-Object -First 1 -Last 0
$Last_Last=$get_childitem_match | Select-Object -First 0 -Last 1
Write-Host "first ="$Last_First
Write-Host "last ="$Last_Last
```
実行例

```PowerShell
first = a
last = l
```
　　

 - 文字列中の任意の箇所の最初と最後を取得

```powershell:SubString.ps1
###続き$path=C:\user\xyz
##ファイル名の任意の要素(4文字目から6文字目)を取得
$get_childitem_match_substring=(Get-ChildItem  $path\*dbg).BaseName | Foreach-Object { if($_.SubString(3,5) -match "...") {$Matches[0]} }
#取得した要素の最初と最後を変数に格納
$substring_first=$get_childitem_match_substring | Select-Object -First 1 -Last 0
$substring_last=$get_childitem_match_substring | Select-Object -First 0 -Last 1
Write-Host "first ="$substring_first
Write-Host "last ="$substring_last
```
実行例

```PowerShell
first = 1ag
last = 5xb
```
正規表現とSubStringを組み合わせてみたが、正規表現だけでどうにかならないか...

##正規表現のグループを使う
今回の場合例えば`SubString(3,5)`を表現したいときは

```PowerShell
Foreach-Object { if($_. -match "(\d{3})(.{3})") {$Matches[2]} }
```
もしくは、他のファイル名のパターンにもマッチしようとするならば

```PowerShell
Foreach-Object { if($_. -match "(.{3})(.{3})") {$Matches[2]} }
```
こんな感じだろうか
　　

###まてよ...?

```PowerShell
Foreach-Object { if($_. -match "(^\d+)(.$)") {$Matches[0]} }
```
と思いやってみたが、何も表示されなかった...



```PowerShell
Foreach-Object { if($_. -match "(^\d+)(\w{1,})(.$)") {$Matches[1],$Matches[3]} }
```
これならよさそうだけど、`$Matches[1]`と`$Matches[3]`それぞれ最初と最後の取得が上手くできないか...残念


##参考

 - [PowerShell/ファイル数・ディレクトリ数をカウントする方法](https://win.just4fun.biz/?PowerShell/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E6%95%B0%E3%83%BB%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E6%95%B0%E3%82%92%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)
 - [PowerShell/PowerShellでLeft,Right,Midをしたい場合・Substring](https://win.just4fun.biz/?PowerShell/PowerShell%E3%81%A7Left%2CRight%2CMid%E3%82%92%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88%E3%83%BBSubstring)

 - [正規表現言語 - クイック リファレンス](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/regular-expression-language-quick-reference)

 - [技術的なこと、あれこれ powershell 入門 条件 IF](http://www.sakutyuu.com/technology/?p=117)

##余談

@コマンドプロンプトでいうpauseみたいなのがないかなとか思ってたけど、そのまま`pause`を入力するとEnterキーの入力待ちになるようです
`Read-Host "続行するには、Enter キーを押してください..." `
でもいいみたい
詳しくは以下のサイト様など
[PowerShell でコマンドプロンプトの Pause コマンドっぽいヤツ](http://neos21.hatenablog.com/entry/2017/01/12/014803)

##最後に
実際にこのコードが何に使えるのかは知る由もありませんが、何かしらのアイデアの糧になればと思います
正規表現と比較演算子で結構いろんなことができるのでもっと使えるようになりたい
パワーシェルの実行ファイル(.ps1)の名前にカッコ()を使うと特殊な挙動になるのが謎

- :arrow_backward: [前回の記事](https://qiita.com/kurosworld/items/1946babccd98b4702835) | [次回の記事](https://qiita.com/kurosworld/items/7dcc28ce6f9f4b9bdbef) :arrow_forward: