Powershell入門 - 08. 文字列、パス文字列の操作

今回は、Powershellの基本操作として文字列の操作と、実際のアプリケーションを想定したパス文字列の操作方法について説明します。

# 1. 文字列の操作
## 1.1. 文字列の定義の仕方
まず、Powershellの文字列はダブルクォーテーション(")か シングルクォーテーション(')を使って定義します。
```powershell
$a = 'test'
$b = "test"
```
上の例では2つの書き方に違いはないですが、ダブルクォーテーション(")の場合は**文字列内に変数を埋め込むことが出来ます。**

```powershell
$x = 5 + 3
$a = '5 + 3 = ${x}'
$b = "5 + 3 = ${x}"
Write-Host $a        # 5 + 3 = ${x}
Write-Host $b        # 5 + 3 = 8
```

変数を埋め込む場合は、`${変数名}`のように変数名を波かっこ({})で括る必要があります。  
※波かっこなしでも書けるのですが、前後に必ず半角スペースが必要になるので、波かっこありで統一して覚えましょう。

---
#### 例題1.
以下のプログラムを修正し、`入力されたの文字列は「XXXXX」です`と表示する処理を書きなさい。
ただし、XXXXXには変数inputの値が入るものとする

```powershell
$input = Read-Host "文字列を入力"
Write-Host $input
```

#### 例題2.
以下の文字列を表示する処理を書きなさい。  
ただし、XXXXXにはカレントディレクトリのパスが入るものとする

`現在私は「XXXXX」にいます。`

---

## 1.2.  文字列の結合
文字列の結合の方法はいくつかあります。  
上で紹介した変数の埋め込みも解法の1つです。
```powershell
$x = "abc"
$y = "def"
Write-Host "${x}${y}"     # abcdef
Write-Host "${x},${y}"    # abc,def
```

ただし、上記の方法はあくまで結合として使うことも出来るというだけで、結合の文脈で紹介されることはあまりありません。
文字列の結合として紹介されるのは、`+`や`+=`を使った方法になります。  

```powershell
$x = "abc"
$y = "def"
Write-Host ($x + $y)          # abcdef
Write-Host ($x + "," + $y)    # abc,def
$x += $y
Write-Host ($x)               # abcdef 
```

---
#### 例題3.
Read-Hostを使って文字列を2つ受け取り、変数x,yに格納しなさい。  
変数xとyを半角スペース区切りで結合した文字列を表示しなさい。

#### 例題4.
以下の文字列配列の要素をハイフン(-)区切りで結合した文字列を作成し、表示しなさい。
```powershell
$arr = "abc","def","ghi","jkl","mno","pqr","stu","vwx","yz"
```
---

例題4をfor文を使って実装するのに苦戦した方もいるかもしれません。
実は、結合する対象が配列に格納されている場合、`Join`メソッドを利用することでより簡単に書くことが出来ます。
```powershell
[string]::Join(区切り文字, 文字列配列)
```
Joinを使って例題4を解くと、以下のようになります。

```powershell
$arr = "abc","def","ghi","jkl","mno","pqr","stu","vwx","yz"
Write-Host ([string]::Join("-", $arr))
```

## 1.3. 文字列用メソッド
Powershellにおいて、文字列は「文字列型のオブジェクト」として扱われています。  
この文字列型のオブジェクトは文字列操作のための様々なメソッドをもっています。
以下はその一例になります。

|メソッド|説明| コマンド例 | 出力例 |
|:--|:--|:-- | :-- |
|str.Substring(i,d) | 文字列strの位置iからd文字分だけ文字を切り出す| "abcde".Substring(2,2) | "cd" |
|str.Replace(x,y) |文字列strに含まれる文字列xをyに置き換える| "My name is XXXX".Replace("XXXX", "POCHI")| "My name is POCHI"
|str.IndexOf(x)|文字列strに含まれるxの位置を返す。ない場合は-1| "abcdefg".IndexOf("cde")| 2 |
|str.Split(x)|文字列strをxで分割した配列を返す| "My name is XXXX".Split( )| 配列["My","name","is","XXXX"]|
|str.ToUpper()|文字列strに含まれる小文字を大文字に変換| "My name is XXXX".ToUpper()| "MY NAME IS XXXX"|
|str.ToLower()|文字列strに含まれる大文字を小文字に変換|"My name is XXXX".ToLower()|"my name is xxxx"|
|str.Trim()|文字列strの前後の空白を削除する|"    abc   ".Trim()| "abc"|

---
#### 例題5.
以下の文字列から"at"の位置を調べ、その開始位置を表示する処理を書きなさい。

`"I can accept failure, everyone fails at something. But I can’t accept not trying."`

#### 例題6.
以下の文字列に含まれる数字をすべて足し合わせた結果を表示する処理を書きなさい。

`"2,4,6,8,10,12,14,16,18,20"`

文字列を整数に変換する処理は、以下を参考にすること
```powershell
$x_str = "15"
$y_str = "2"
$x = [int]$x_str
$y = [int]$y_str

Write-Host ($x * $y)           # 30
```
---

# 2. パスの操作
`C:\tmp`や`./work`などの文字列を**パス文字列**といいます。  
powershellはWindowsのファイル操作を自動化するなどの目的で使われることが多いです。
そのため、文字列の中でもパス文字列の操作は非常に重要になります。

## 2.1. パス文字列の結合(Join-Path)
Powershellで自動化処理を実装すると、フォルダパス同士を結合して新しいフォルダパスを取得したいケースが良くあります。
こういったときは、`Join-Path`を使うことをお勧めします。

```powershell
Join-Path -Path "D:\Powershell\data\" -ChildPath "sample1.py"
```

単純に`+`で結合すればいいのではと思った人、上記の例では確かにその通りです。  
では、以下の例を見てみましょう。

```powershell
$x = Join-Path -Path "D:\Powershell\data" -ChildPath "sample1.py"
$y = "D:\Powershell\data" + "sample1.py"
Write-Host $x   # D:\Powershell\data\sample1.py
Write-Host $y   # D:\Powershell\datasample1.py
```

`+`による結合は単純に文字列を結合します。
そのため、パス同士の区切り文字である`\`があろうとなかろうとそのまま接続してしまいます。

一方で、Join-Pathの場合は、\の過不足まで含めてチェックしてくれるので、安全にパス文字列どうしを結合できます。
もし、パス文字列の結合をしたい場合、必ずこちらを使うようにしましょう。

---
#### 例題7.
以下の2つのパス文字列を結合しなさい。
- D:\Powershell
- data\sampleFolder

#### 例題8.
以下の2つの変数を使って、`D:\Powershell\data`直下にtmpフォルダを作成しなさい。  
フォルダの作成方法は[こちら](https://qiita.com/daifukusan/items/ce474e3eee7d827ce55d)

```powershell
$path = "D:\Powershell\data"
$folder = "tmp"
```
---

## 2.1. パス文字列の抽出(Split-Path)
パス文字列から特定の部分だけを切り抜きたい場合`Split-Path`を使います。

```powershell
$path = "D:\Powershell\data"

Split-Path -Path $path -Qualifier       # D:
Split-Path -Path $path -NoQualifier     # powershell\data
Split-Path -Path $path -Parent          # D:\powershell
Split-Path -Path $path -Leaf            # data
```

`Split-Path`は指定するパラメータによって、パスからどの部分を切り取るかが変わります。

|パラメータ|取得する部分|
|:--|:--|
|-Qualifier | ドライブ名 |
|-NoQualifier | ドライブ名以外 |
|-Parent | 	親フォルダのパス |
|-Leaf | 末端の要素 |

この処理を使うと出来ることの幅が広がります。
例えば、`D:\Powershell`以下のフォルダで、sample3.txtのあるフォルダを取得する場合は以下のようになります。

```powershell
$list = Get-ChildItem -Path "D:\Powershell" -Recurse

foreach($el in $list){
    if($el.Name -eq "sample3.txt"){
        $parent = Split-Path -Path $el.FullName -Parent
        Write-Host $parent
    }
}
```

---
#### 例題9.
以下のコマンドを使って、カレントディレクトリを変更しなさい。
```powershell
Set-Location D:\Powershell\data
```

カレントディレクトリの親フォルダを取得する処理を書きなさい。

#### 例題10.
例題9を修正し、カレントディレクトリの親フォルダへ移動する処理を書きなさい。

## 2.3. パスの存在をチェックする(Test-Path)
Join-Pathなどで作成したパス文字列が存在するかどうかを確認したいときは`Test-Path`コマンドレットを使うとよいです。
```powershell
$newPath = Join-Path -Path "D:\Powershell\data" -ChildPath "tmp"
Test-Path -Path $newPath    # True or False
```

`Test-Path`はパスが存在する場合は`True`、存在しなければ`False`を返します。
これとif文を組み合わせることで、フォルダがなければ新しくフォルダを作成、ある場合は何もしないなどの処理を組むことが出来ます。

#### 例題11.
以下のコマンドを使って、カレントディレクトリを変更しなさい。
```powershell
Set-Location D:\Powershell\data
```
カレントディレクトリの直下にsample1.txtがあるかどうか確認し、ある場合はOKと表示しなさい。

Prev : [07. ループステートメント](./fcb962e20acffac54951)
Next : [09. パイプライン](./53cb7275a044be3256aa)

[目次へ戻る](./926b8053a031539437a3)