BASHとPowerShellのフィルタ処理まとめ④

BASHとPowerShellのフィルタ処理例をまとめていきます。下記記事たちの続きですので、フィルタそのものの説明、目的、検証環境などの前置きは記事①をご参照ください。

[■BASHとPowerShellのフィルタ処理まとめ①](https://qiita.com/HOBO0222/items/377ae7f4f229f49c5475)
[■BASHとPowerShellのフィルタ処理まとめ②](https://qiita.com/HOBO0222/items/08a669f494ace4c6c7b8)
[■BASHとPowerShellのフィルタ処理まとめ③](https://qiita.com/HOBO0222/items/abaf25087de429e76c04)

①と②は主に文字列のフィルタ処理についてまとめ、
③は主に行に関するフィルタ処理をまとめました。
本記事④では主に列に関するフィルタ処理をまとめていきます。

テスト用のフィルタするファイルは以下を使用します。
列数は全部で9列です。

```personal_infomation05.csv
連番,氏名,氏名（ローマ字）,性別,生年月日,年齢,出身地,血液型,パスワード
1,松川一司,MatsukawaKazushi,男,1953/12/08,64,佐賀県,AB,"F,cPncvO"
2,草野金弥,KusanoKinya,男,1989/09/16,28,山形県,O,"ik,PRAhi"
3,馬場長蔵,BabaChouzou,男,1979/07/26,38,神奈川県,O,"SF,tUs5v"
4,岩城留美,IwakiRumi,女,1986/10/26,31,佐賀県,A,pBPMWHu-
5,木下泰史,KinoshitaYasufumi,男,1987/10/05,30,鹿児島県,B,5qyMCGBX
6,笠原俊行,KasaharaToshiyuki,男,1991/04/22,27,山形県,O,9AZkbaHa
7,北柚季,KitaYuzuki,女,1950/01/13,68,青森県,O,vSEacKGd
8,梶田圭一,KajitaKeiichi,男,1962/02/22,56,静岡県,A,OkxZKZ4o
9,小久保光希,KokuboMitsuki,女,1993/04/01,25,福島県,O,"u4j,696x"
10,石渡佑奈,IshiwatariYuuna,女,1951/11/09,66,兵庫県,AB,nZzIx7Ej
```
ではぽんぽんとフィルタ紹介と比較をしていきます。

##【任意の列のみをフィルタで取り出す】
まずは基本のパターンです。

###◆BASHの場合
BASHではcutとawkの二つの方法をご紹介します。まずはcutからです。

```BASH:cutで列フィルタ
[root@centos74 log]# cat personal_infomation05.csv | cut -d "," -f 2,4-6
氏名,性別,生年月日,年齢
松川一司,男,1953/12/08,64
草野金弥,男,1989/09/16,28
馬場長蔵,男,1979/07/26,38
岩城留美,女,1986/10/26,31
木下泰史,男,1987/10/05,30
笠原俊行,男,1991/04/22,27
北柚季,女,1950/01/13,68
梶田圭一,男,1962/02/22,56
小久保光希,女,1993/04/01,25
石渡佑奈,女,1951/11/09,66
```
2行目、4～6行目だけをフィルタで抽出しています。-dでファイルの区切り文字を指定できます。ここでは,(カンマ)を指定していますね。その後、-fで指定の列番号を指定して取り出しています。

実はcutの場合列番号をでたらめに入れても入力した順番ではなくシーケンスで並べて取り出してしまいます。

```BASH:cutででたらめな順番でフィルタ
[root@centos74 log]# cat personal_infomation05.csv | cut -d "," -f 9,4,7,2,1,3
連番,氏名,氏名（ローマ字）,性別,出身地,パスワード
1,松川一司,MatsukawaKazushi,男,佐賀県,"F
2,草野金弥,KusanoKinya,男,山形県,"ik
3,馬場長蔵,BabaChouzou,男,神奈川県,"SF
4,岩城留美,IwakiRumi,女,佐賀県,pBPMWHu-
5,木下泰史,KinoshitaYasufumi,男,鹿児島県,5qyMCGBX
6,笠原俊行,KasaharaToshiyuki,男,山形県,9AZkbaHa
7,北柚季,KitaYuzuki,女,青森県,vSEacKGd
8,梶田圭一,KajitaKeiichi,男,静岡県,OkxZKZ4o
9,小久保光希,KokuboMitsuki,女,福島県,"u4j
10,石渡佑奈,IshiwatariYuuna,女,兵庫県,nZzIx7Ej
```

そこで指定した順番に取り出したい場合はawkを使います。以下の通りです。

```BASH:awkで列フィルタ
[root@centos74 log]# cat personal_infomation05.csv | awk -F "," '{print $9,$4,$7,$2,$1,$3}'
パスワード 性別 出身地 氏名 連番 氏名（ローマ字）
"F 男 佐賀県 松川一司 1 MatsukawaKazushi
"ik 男 山形県 草野金弥 2 KusanoKinya
"SF 男 神奈川県 馬場長蔵 3 BabaChouzou
pBPMWHu- 女 佐賀県 岩城留美 4 IwakiRumi
5qyMCGBX 男 鹿児島県 木下泰史 5 KinoshitaYasufumi
9AZkbaHa 男 山形県 笠原俊行 6 KasaharaToshiyuki
vSEacKGd 女 青森県 北柚季 7 KitaYuzuki
OkxZKZ4o 男 静岡県 梶田圭一 8 KajitaKeiichi
"u4j 女 福島県 小久保光希 9 KokuboMitsuki
nZzIx7Ej 女 兵庫県 石渡佑奈 10 IshiwatariYuuna
```
-Fで区切り文字を指定したあと、printで抽出したい列番号を```$n```で表現して指定しています。awkは便利で、読み込んだテキストファイルをレコードに自動的に分割しますが、さらにそのレコードをフィールドというものに分割します。具体的には、1つのレコードの中を組込変数FS（Field Separator）で分割したものをフィールドと言います。この組込変数FSはデフォルトではタブで設定されていますが-Fオプションで任意の区切り文字で区切るよう指定することができます。

###◆PowerShellの場合
次にPowerShellです。おそらく使い手によって様々な方法がありますが、ここではforeachと配列、フォーマット演算子を利用して列フィルタを行います。

```PowerShell:PowerShellでの列フィルタ
PS C:\Tools\logs> $DATA=Get-Content -Encoding UTF8 .\personal_infomation05.csv | ConvertFrom-String -Delimiter ","
PS C:\Tools\logs> $DATA | foreach { "{0},{1},{2},{3}" -f $_.P2,$_.P4,$_.P5,$_.P6 }
氏名,性別,生年月日,年齢
松川一司,男,1953/12/08 0:00:00,64
草野金弥,男,1989/09/16 0:00:00,28
馬場長蔵,男,1979/07/26 0:00:00,38
岩城留美,女,1986/10/26 0:00:00,31
木下泰史,男,1987/10/05 0:00:00,30
笠原俊行,男,1991/04/22 0:00:00,27
北柚季,女,1950/01/13 0:00:00,68
梶田圭一,男,1962/02/22 0:00:00,56
小久保光希,女,1993/04/01 0:00:00,25
石渡佑奈,女,1951/11/09 0:00:00,66
```
前回記事でも書いた通り、PowerShellは基本的にオブジェクトとして処理を行いますので、パイプ処理もオブジェクトをパイプで受け渡す形になります。この点は文字列を受け渡すBASHと根本的に異なる点です。

ConvertFrom-Stringは文字列を任意の区切り文字に分割し、オブジェクトに格納するコマンドレットです。処理対象ファイルのCSVファイルがオブジェクトではないため、まずはオブジェクトとして格納する処理を行います。その後foreachで任意の列をさらにカンマ区切りで出力されるよう整形して取り出すようにしています。

ちなみにConvertFrom-CSVでオブジェクト格納すると1行目を見出し行として認識してくれます。こちらの方が列指定が直感的でわかりやすいのでうまく使い分けてください。

```POWERSHLL:ConvertFrom-Csv
PS C:\Tools\logs> $DATA=Get-Content -Encoding UTF8 .\personal_infomation05.csv | ConvertFrom-Csv
PS C:\Tools\logs> $DATA | foreach { "{0},{1},{2},{3}" -f $_.氏名,$_.性別,$_.生年月日,$_.年齢 }
松川一司,男,1953/12/08,64
草野金弥,男,1989/09/16,28
馬場長蔵,男,1979/07/26,38
岩城留美,女,1986/10/26,31
木下泰史,男,1987/10/05,30
笠原俊行,男,1991/04/22,27
北柚季,女,1950/01/13,68
梶田圭一,男,1962/02/22,56
小久保光希,女,1993/04/01,25
石渡佑奈,女,1951/11/09,66
```

##【ソート】
###◆BASHの場合
sortを使って行います。生年月日を対象にソートしていきます。

```BASH:生年月日を昇順でソート
[root@centos74 log]# cat personal_infomation05.csv | sort -t "," -k5
7,北柚季,KitaYuzuki,女,1950/01/13,68,青森県,O,vSEacKGd
10,石渡佑奈,IshiwatariYuuna,女,1951/11/09,66,兵庫県,AB,nZzIx7Ej
1,松川一司,MatsukawaKazushi,男,1953/12/08,64,佐賀県,AB,"F,cPncvO"
8,梶田圭一,KajitaKeiichi,男,1962/02/22,56,静岡県,A,OkxZKZ4o
3,馬場長蔵,BabaChouzou,男,1979/07/26,38,神奈川県,O,"SF,tUs5v"
4,岩城留美,IwakiRumi,女,1986/10/26,31,佐賀県,A,pBPMWHu-
5,木下泰史,KinoshitaYasufumi,男,1987/10/05,30,鹿児島県,B,5qyMCGBX
2,草野金弥,KusanoKinya,男,1989/09/16,28,山形県,O,"ik,PRAhi"
6,笠原俊行,KasaharaToshiyuki,男,1991/04/22,27,山形県,O,9AZkbaHa
9,小久保光希,KokuboMitsuki,女,1993/04/01,25,福島県,O,"u4j,696x"
連番,氏名,氏名（ローマ字）,性別,生年月日,年齢,出身地,血液型,パスワード
```
-tで区切り文字を指定し、-knで並べ替える列を指定します。記載していませんが-rをつければ降順で並べ替えることもできます。

次は「性別で昇順してから、生年月日を降順」で並べ替えます。

```BASH:性別で昇順してから、生年月日を降順でソート
[root@centos74 log]# cat personal_infomation05.csv | sort -t "," -k4,4 -k5,5r
9,小久保光希,KokuboMitsuki,女,1993/04/01,25,福島県,O,"u4j,696x"
4,岩城留美,IwakiRumi,女,1986/10/26,31,佐賀県,A,pBPMWHu-
10,石渡佑奈,IshiwatariYuuna,女,1951/11/09,66,兵庫県,AB,nZzIx7Ej
7,北柚季,KitaYuzuki,女,1950/01/13,68,青森県,O,vSEacKGd
連番,氏名,氏名（ローマ字）,性別,生年月日,年齢,出身地,血液型,パスワード
6,笠原俊行,KasaharaToshiyuki,男,1991/04/22,27,山形県,O,9AZkbaHa
2,草野金弥,KusanoKinya,男,1989/09/16,28,山形県,O,"ik,PRAhi"
5,木下泰史,KinoshitaYasufumi,男,1987/10/05,30,鹿児島県,B,5qyMCGBX
3,馬場長蔵,BabaChouzou,男,1979/07/26,38,神奈川県,O,"SF,tUs5v"
8,梶田圭一,KajitaKeiichi,男,1962/02/22,56,静岡県,A,OkxZKZ4o
1,松川一司,MatsukawaKazushi,男,1953/12/08,64,佐賀県,AB,"F,cPncvO"
```
sortコマンドは```-k```オプションを使いこなすことによって幅が広がります。```-k```は、

```BASH:
-k 列1,列2[r]
```
と指定することにより指定範囲内の列を昇順か降順で指定することができます。
あと```-n```のお話しもしたかったですが他の方の記事に譲ります。

###◆PowerShellの場合
Sort-Objectを使って行います。

```POWERSHELL:PowerShellでの生年月日でソート
PS C:\Tools\logs> $DATA=Get-Content -Encoding UTF8 .\personal_infomation05.csv | ConvertFrom-String -Delimiter ","
PS C:\Tools\logs> $DATA | Sort-Object -Property P5 | foreach { "{0},{1},{2},{3},{4:yyyy/MM/dd},{5},{6},{7},{8}" -f $_.P1,$_.P2,$_.P3,$_.P4,$_.P5,$_.P6,$_.P7,$_.P8,$_.P9 }
7,北柚季,KitaYuzuki,女,1950/01/13,68,青森県,O,vSEacKGd
10,石渡佑奈,IshiwatariYuuna,女,1951/11/09,66,兵庫県,AB,nZzIx7Ej
1,松川一司,MatsukawaKazushi,男,1953/12/08,64,佐賀県,AB,"F
8,梶田圭一,KajitaKeiichi,男,1962/02/22,56,静岡県,A,OkxZKZ4o
3,馬場長蔵,BabaChouzou,男,1979/07/26,38,神奈川県,O,"SF
4,岩城留美,IwakiRumi,女,1986/10/26,31,佐賀県,A,pBPMWHu-
5,木下泰史,KinoshitaYasufumi,男,1987/10/05,30,鹿児島県,B,5qyMCGBX
2,草野金弥,KusanoKinya,男,1989/09/16,28,山形県,O,"ik
6,笠原俊行,KasaharaToshiyuki,男,1991/04/22,27,山形県,O,9AZkbaHa
9,小久保光希,KokuboMitsuki,女,1993/04/01,25,福島県,O,"u4j
連番,氏名,氏名（ローマ字）,性別,生年月日,年齢,出身地,血液型,パスワード
```
ConvertFrom-Stringでオブジェクトに格納した後、Sort-Objectコマンドレットを使っています。これはプロパティ値に基づいてオブジェクトを並べ替えます。```-Property```パラメータには、並べ替えのキーとなるプロパティ名、または集計プロパティの連想配列(ハッシュテーブル)を1つまたは複数指定します。集計プロパティで有効なキーは次の表のとおりです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/198785/38e6ad63-9fa0-c461-8aeb-67c1c6c6edab.png)

BASHで行った「性別で昇順してから、生年月日を降順」で並べ替えるとこうなります。

```POWERSHELL:PowerShellで「性別で昇順してから、生年月日を降順」にソート
PS C:\Tools\logs> $DATA | Sort-Object -Property `
>> @{Expression="P4";Ascending=$True},
>> @{Expression="P5";Ascending=$False} | foreach { "{0},{1},{2},{3},{4:yyyy/MM/dd},{5},{6},{7},{8}" -f $_.P1,$_.P2,$_.P3,$_.P4,$_.P5,$_.P6,$_.P7,$_.P8,$_.P9 }
9,小久保光希,KokuboMitsuki,女,1993/04/01,25,福島県,O,"u4j
4,岩城留美,IwakiRumi,女,1986/10/26,31,佐賀県,A,pBPMWHu-
10,石渡佑奈,IshiwatariYuuna,女,1951/11/09,66,兵庫県,AB,nZzIx7Ej
7,北柚季,KitaYuzuki,女,1950/01/13,68,青森県,O,vSEacKGd
連番,氏名,氏名（ローマ字）,性別,生年月日,年齢,出身地,血液型,パスワード
6,笠原俊行,KasaharaToshiyuki,男,1991/04/22,27,山形県,O,9AZkbaHa
2,草野金弥,KusanoKinya,男,1989/09/16,28,山形県,O,"ik
5,木下泰史,KinoshitaYasufumi,男,1987/10/05,30,鹿児島県,B,5qyMCGBX
3,馬場長蔵,BabaChouzou,男,1979/07/26,38,神奈川県,O,"SF
8,梶田圭一,KajitaKeiichi,男,1962/02/22,56,静岡県,A,OkxZKZ4o
1,松川一司,MatsukawaKazushi,男,1953/12/08,64,佐賀県,AB,"F
```
綺麗に書いているのでかなり長くなってしまいますが、エイリアスを駆使すると短く書けます。

```POWERSHELL:PowerShell、エイリアスを使った場合
PS C:\Tools\logs> $DATA | Sort-Object -Property `
>> @{E="P4";A=$True},
>> @{E="P5";D=$True} | foreach { "{0},{1},{2},{3},{4:yyyy/MM/dd},{5},{6},{7},{8}" -f $_.P1,$_.P2,$_.P3,$_.P4,$_.P5,$_.P6,$_.P7,$_.P8,$_.P9 }
9,小久保光希,KokuboMitsuki,女,1993/04/01,25,福島県,O,"u4j
4,岩城留美,IwakiRumi,女,1986/10/26,31,佐賀県,A,pBPMWHu-
10,石渡佑奈,IshiwatariYuuna,女,1951/11/09,66,兵庫県,AB,nZzIx7Ej
7,北柚季,KitaYuzuki,女,1950/01/13,68,青森県,O,vSEacKGd
連番,氏名,氏名（ローマ字）,性別,生年月日,年齢,出身地,血液型,パスワード
6,笠原俊行,KasaharaToshiyuki,男,1991/04/22,27,山形県,O,9AZkbaHa
2,草野金弥,KusanoKinya,男,1989/09/16,28,山形県,O,"ik
5,木下泰史,KinoshitaYasufumi,男,1987/10/05,30,鹿児島県,B,5qyMCGBX
3,馬場長蔵,BabaChouzou,男,1979/07/26,38,神奈川県,O,"SF
8,梶田圭一,KajitaKeiichi,男,1962/02/22,56,静岡県,A,OkxZKZ4o
1,松川一司,MatsukawaKazushi,男,1953/12/08,64,佐賀県,AB,"F
```
...それでもBASHに比べると長いですが。。

また、foreach内のスクリプトブロックのフォーマット演算子で全ての列項目を明示的に記載しているのはSQL文で各列をSELECT～FROM内で記載するのと同じイメージで、必ず記載する必要があります(たぶん。いらないという場合はぜひ教えてくださると助かります)。フォーマット演算子については後ほど説明します。

##【列項目の書式を変更する方法】
列の書式を綺麗に変更したい場合があると思います。

###◆BASHの場合
まず年齢列のデータの語尾全てに「歳」を付与する場合です。

```BASH:「年齢」列に「歳」をつける
[root@centos74 log]# cat personal_infomation05.csv | awk -F "," '{print $1,$2,$3,$4,$5,$6"歳",$7,$8,$9}' | sed 's/年齢歳/年齢/'
連番 氏名 氏名（ローマ字） 性別 生年月日 年齢 出身地 血液型 パスワード
1 松川一司 MatsukawaKazushi 男 1953/12/08 64歳 佐賀県 AB "F
2 草野金弥 KusanoKinya 男 1989/09/16 28歳 山形県 O "ik
3 馬場長蔵 BabaChouzou 男 1979/07/26 38歳 神奈川県 O "SF
4 岩城留美 IwakiRumi 女 1986/10/26 31歳 佐賀県 A pBPMWHu-
5 木下泰史 KinoshitaYasufumi 男 1987/10/05 30歳 鹿児島県 B 5qyMCGBX
6 笠原俊行 KasaharaToshiyuki 男 1991/04/22 27歳 山形県 O 9AZkbaHa
7 北柚季 KitaYuzuki 女 1950/01/13 68歳 青森県 O vSEacKGd
8 梶田圭一 KajitaKeiichi 男 1962/02/22 56歳 静岡県 A OkxZKZ4o
9 小久保光希 KokuboMitsuki 女 1993/04/01 25歳 福島県 O "u4j
10 石渡佑奈 IshiwatariYuuna 女 1951/11/09 66歳 兵庫県 AB nZzIx7Ej
```
awkで年齢列(```$6```)の語尾に「歳」をつけるようにしています。ただこれだと見出し行の「年齢」の語尾にも「歳」がついてしまうので、sedで消すように処理しています。


###◆PowerShellの場合(フォーマット演算子)
PowerShellの場合は以下の通りです。

```POWERSHELL:PowerShellで「歳」を語尾につける
PS C:\Tools\logs> $DATA=Get-Content -Encoding UTF8 .\personal_infomation5.csv | ConvertFrom-Csv
PS C:\Tools\logs> $DATA | foreach { "{0},{1},{2},{3},{4},{5}歳,{6},{7},{8}" -f $_.連番,$_.氏名,$_.'氏名(ローマ字)',$_.性別,$_.生年月日,$_.年齢,$_.出身地,$_.血液型,$_.パスワード }
1,松川一司,,男,1953/12/08,64歳,佐賀県,AB,F,cPncvO
2,草野金弥,,男,1989/09/16,28歳,山形県,O,ik,PRAhi
3,馬場長蔵,,男,1979/07/26,38歳,神奈川県,O,SF,tUs5v
4,岩城留美,,女,1986/10/26,31歳,佐賀県,A,pBPMWHu-
5,木下泰史,,男,1987/10/05,30歳,鹿児島県,B,5qyMCGBX
6,笠原俊行,,男,1991/04/22,27歳,山形県,O,9AZkbaHa
7,北柚季,,女,1950/01/13,68歳,青森県,O,vSEacKGd
8,梶田圭一,,男,1962/02/22,56歳,静岡県,A,OkxZKZ4o
9,小久保光希,,女,1993/04/01,25歳,福島県,O,u4j,696x
10,石渡佑奈,,女,1951/11/09,66歳,兵庫県,AB,nZzIx7Ej
```

foreachとフォーマット演算子の組み合わせ内のインデックス番号を指定しているスクリプトブロックのすぐ横に任意の文字列をつければ全てのデータに意図した通りに文字列を追加してくれます。なお今回はConvertFrom-Csvで見出し行指定をしていますが、ConvertFrom-Stringで指定した場合はやはり「年齢」に「歳」がついてしまいますので、その場合はReplaceメソッドで文字列を削除してください。

ここからはforeachのスクリプトブロック内で使用しているフォーマット演算子について説明します。かなり便利なのでPowerShellを使う方で知らない人は覚えたほうがいいです。

気になった方もいるかもしれませんが、ConvertFrom-String側では生年月日が```yyyy/mm/dd hh:mm:ss```の書式に勝手に変換されて出力されてしまっています。これはオブジェクトに格納した際に暗黙の型変換で生年月日列の各データをDateTime型と捉えてPowerShellが格納しているためです。この書式、生年月日としては余分ですよね。これを任意の書式に簡単に直す方法があります。以下の構文をご覧ください。

```PowerShell:出力時の書式を整形する方法
PS C:\Tools\logs> $DATA | foreach { "{0},{1},{2:yyyy/MM/dd},{3}" -f $_.P2,$_.P4,$_.P5,$_.P6 }
氏名,性別,生年月日,年齢
松川一司,男,1953/12/08,64
草野金弥,男,1989/09/16,28
馬場長蔵,男,1979/07/26,38
岩城留美,女,1986/10/26,31
木下泰史,男,1987/10/05,30
笠原俊行,男,1991/04/22,27
北柚季,女,1950/01/13,68
梶田圭一,男,1962/02/22,56
小久保光希,女,1993/04/01,25
石渡佑奈,女,1951/11/09,66
```
これで最初のBASHと同じ形で出力が完了しています。整形した部分お分かり頂けるでしょうか。foreachのスクリプトブロック{}内で{2}をyyyy/MM/dd形式で出力するように指定しています。これはフォーマット演算子(-f)を使って左側で指定したインデックス番号の書式を変更できる構文となります。

```PowerShell:フォーマット演算子の構文
<書式指定子> -f <オブジェクト[]>
```

適用できる書式は、書式指定子を使って指定します。書式指定子は様々あり、イメージとしてはMicrosoft Officeでよく出てくるセルの書式設定などでの設定とよく似ています。

例をいくつか下に載せておきます。

```PowerShell:フォーマット演算子の簡単な書式例
PS C:\Tools\logs> "{0:C}" -f 1000 #通貨形式で表示
¥1,000

PS C:\Tools\logs> "{0:N4}" -f 13　#少数点4桁以下を表示
13.0000

PS C:\Tools\logs> "{0:P1}" -f 0.345 #パーセント値で表示
34.5%

PS C:\Tools\logs> "{0:d8}" -f 4453　#8桁になるように残りの桁を「0」で埋め尽くす
00004453

PS C:\Tools\logs> "{0,10}" -f 50　#フィールドの幅を「10」にし、右揃えで表示
        50

PS C:\Tools\logs> "{0,-10}" -f 50 #フィールドの幅を「10」にし、左揃えで表示
50

PS C:\Tools\logs> "{0:#,###}" -f 10000000000 #3桁ごとに「,」で区切る
10,000,000,000

PS C:\Tools\logs> "{2} {1} {0}" -f "1nd","2nd","3nd" #表示順番の指定
3nd 2nd 1nd

PS C:\Tools\logs> "{0:yyyy/MM/dd-hh:mm:ss}" -f (Get-Date) #DateTime値の時間を表示
2018/04/29-04:50:37

PS C:\Tools\logs> "{0:yyyy/MM/dd-hh:mm:ssだよ！}" -f (Get-Date) #文字列の挿入
2018/04/29-04:51:25だよ！
```
その他の書式設定文字列については下記のURLをご参照ください。

[■カスタム数値書式指定文字列](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-numeric-format-strings)
[■カスタム日時書式指定文字列](https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-date-and-time-format-strings)

上記ページも併せて確認してもらうとわかると思いますが、コマンドレットだけでなくメソッドでも使えるためPowerShell内での汎用性はかなり高いです。

ポイントとしてはコマンドレットなどで出力した「オブジェクト」に対して適用されるということであり、ファイルなどまだ各文字列データをオブジェクトに格納していないものを相手にする場合は、今回のようにConvertFrom-Stringなどで各文字列をオブジェクトに格納した後、料理する必要があります。

##【処理結果行ごとに日付-時刻を明記する方法】
スクリプトを作成中に処理ごとに日付と時刻を行出力させることは多いと思います。双方の言語でどのように実施するか以下に記載します。今回はファイルではなく任意のコマンドを整形していきます。出力させる時間の書式形式は以下のイメージで出していきます。

```出力イメージ
[yyyy/MM/dd-hh:mm:ss]出力結果
```

###◆BASHの場合
BASHはvmstatコマンドの一行一行に時間を出力させます。以下の通りです。

```BASH:vmstatコマンド
[root@centos74 log]# vmstat -n 1 5 | awk '{print strftime("[%Y/%m/%d-%H:%M:%S]"),$0}'
[2018/04/29-07:53:22] procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
[2018/04/29-07:53:22]  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
[2018/04/29-07:53:22]  0  0 198764  92492      0 376080    0    0  1307    89    3    1  1  1 98  0  0
[2018/04/29-07:53:23]  0  0 198764  89584      0 376100    0    0     0    49  362  388  1  2 96  1  0
[2018/04/29-07:53:24]  0  0 198764  89336      0 376100    0    0     0    40  340  356  1  1 99  0  0
[2018/04/29-07:53:25]  0  0 198764  87552      0 376136    0    0     0    60  330  370  1  2 98  1  0
[2018/04/29-07:53:26]  0  0 198764  87552      0 376136    0    0     0    62  248  367  1  1 98  1  0
```

awkはstrftime( 書式文字列 [, 時刻] )で時刻を文字列に変換できます。さらに$0でvmstatの結果を指して一行で結果ログを出すようにしています。便利なのでこれはよく使っています。

###◆PowerShellの場合
PowerShellはリソース取得にはGet-WmiObjectを利用することが多いです。今回はこれを利用してCPU使用率を取得し、さらにカンマ区切りで出力されるようにしてみます。以下の通りです。

```POWERSHELL:PowerShellでCPU使用率を取得
PS C:\Tools\logs> Get-WmiObject Win32_PerfFormattedData_PerfOS_Processor | Select Name,PercentProcessorTime

Name   PercentProcessorTime
----   --------------------
_Total                   27
0                        32
1                        13
2                        26
3                        38


PS C:\Tools\logs> $CPUPer=Get-WmiObject Win32_PerfFormattedData_PerfOS_Processor | Select Name,PercentProcessorTime
PS C:\Tools\logs> $CPUPer | foreach { "{0:[yyyy/MM/dd-HH:mm:ss]},{1},{2}" -f (Get-Date),$_.Name,$_.PercentProcessorTime }
[2018/04/29-08:21:53],_Total,14
[2018/04/29-08:21:53],0,25
[2018/04/29-08:21:53],1,18
[2018/04/29-08:21:53],2,6
[2018/04/29-08:21:53],3,6
```

PowerShellはフォーマット演算子のインデックス番号0の位置にGet-Dateを配置して、さらに```{0:[yyyy/MM/dd-HH:mm:ss]}```で指定すれば時間ごとにリソースを出力できるようになります。上記では全体のCPU使用率(_Total)とコア毎の個別のCPU使用率が出ています。

##【①～④までの方法を組み合わせると色々できます】
今までに紹介した行と列のフィルタ処理と合わせると好きな文字列を実行時刻と合わせて抽出することができるようになります。

###◆BASHの場合
BASHは事例としてfreeコマンドのMEMのused(物理メモリ使用量)の値だけ取得してみます。

```BASH:freeコマンドのフィルタ
[root@centos74 log]# free
              total        used        free      shared  buff/cache   available
Mem:         999720      540304       82128       52300      377288      208888
Swap:       1572860      198764     1374096
[root@centos74 log]# free | awk '{print $3}' | sed -n '2p' | awk '{print strftime("[%Y/%m/%d-%H:%M:%S]"),$0}'
[2018/04/29-08:04:49] 540608
[root@centos74 log]# free | awk '{print $3}' | sed -n '2p' | awk '{print strftime("[%Y/%m/%d-%H:%M:%S]"),$0}'
[2018/04/29-08:04:53] 540200
```
パイプを3回使って処理しています。

1回目の処理(```awk '{print $3}'```)ではfreeコマンドの３列目を抜き出してきています。結果としては以下の通りです。

```BASH:1回目
[root@centos74 log]# free | awk '{print $3}'
free
532836
198764
```
1行目がusedではなくfreeなのは1行目だけ1番目の列に見出しがなく1つ分ズレているためです。

2回目の処理(```sed -n '2p'```)は2行目だけを取り出しています。

```BASH:2回目
[root@centos74 log]# free | awk '{print $3}' | sed -n '2p'
533352
```
あとは3回目で先ほど記載した時刻指定を付与しています。こんな感じで今まで①～④で紹介した方法を組み合わせると任意の値を取り出すことは簡単にできるようになります。

###◆PowerShellの場合
PowerShellはCドライブの空き容量のパーセンテージを抜き取りたいと思います。

```POWERSHELL:PowerShell、Cドライブの空き容量のパーセンテージ
PS C:\Tools\logs> Get-WmiObject Win32_PerfFormattedData_PerfDisk_LogicalDisk | select Name,PercentFreeSpace

Name            PercentFreeSpace
----            ----------------
_Total                        10
C:                             9
HarddiskVolume4               67
HarddiskVolume5                7
PS C:\Users\TASK-PC> $DISKPer=Get-WmiObject Win32_PerfFormattedData_PerfDisk_LogicalDisk | select Name,PercentFreeSpace
PS C:\Users\TASK-PC> "{0:[yyyy/MM/dd-HH:mm:ss]}{1}" -f (Get-Date) , $DISKPer.PercentFreeSpace[1]
[2018/04/29-13:01:33]9
```
最終的に```$DISKPer.PercentFreeSpace[1]```だけで意図した値のみを取り出せるようになります。意味としては「PercentFreeSpaceのインデックス番号1を表示する」ようにしています。

余談ですが「9％しかないのはまずいな...」といま気づいて焦っています。
ちなみに語尾に%を付けたい場合は以下の通りにToStringでメソッドチェーンします。

```POWERSHELL:
PS C:\Users\TASK-PC> "{0:[yyyy/MM/dd-HH:mm:ss]}{1}" -f (Get-Date) , $DISKPer.PercentFreeSpace[1].ToString("0\%") #%が特殊文字のためエスケープ文字挿入
[2018/04/29-13:02:35]9%
```

こんな感じで特定のリソース値を取得することは、ここまでの方法を知ってる方であれば容易かと思います。あとはBASH側ならcron、PowerShell側ならタスクスケジューラ、統合監視ツールなら設定として入れ込むなりして定期的にリソース取得してもらえればと思います。

##【次回とこれまでの感想】
実は今回で計算、集計系も入れ込もうと思っていましたが、想像以上に長くなってしまったので次回⑤でまとめます。

ここまでまとめてみて、③の最後でも話しましたがBASHはテキストフィルタにかなり特化しており、ワンライナーで収めるのにかなり適しています。PowerShellは(BASHに比べて)ワンライナーで収める処理は劣りますが、配列(特に連想配列)とメソッドチェーン、フォーマット演算子、スクリプトブロックを駆使し始めるとデータ処理の幅がかなり拡がります。

BASHは「一つのコマンドの汎用性」が幅広いため、10個ほどコマンドの使い方を覚えてパイプすればかなりの状況に対応できてしまいます。例えば、「新しいコマンドが出てきたよ！」というニュースが来て確認してみても、「でもこれ、あれとあれとあれとパイプすれば同じことできるし、いらなくない？」となることがしばしばです。汎用性が高いおかげで他人のコードを見てもだいたい同じ書き方なので可読性も高く、非常に取っつき易いシンプルな言語です。覚えて損はないです。

一方PowerShellはコマンド10個覚えるだけではまだまだ使いきれません。そこからさらにオブジェクト処理の仕方(先に述べた配列やメソッドチェーン及び関数)も覚えると格段に処理の仕方が増えます。それでも1カ月も触ってるとかなり使えるようになっていると思います。現に私はまだ触り始めて1カ月半ほどです。

PowerShellは、「複数サーバに対して一斉に複数の情報を任意に指定していつでもリモートで抽出できる」くらいに複雑な処理を求め始めたら、BASHより便利！ってなるのかなと思い始めています。

これからも比較をして遊んでみます。

ここまで閲覧頂きありがとうございました。
なにか追加情報や指摘事項、「もっといい方法あるよ！」などありましたらコメントへお願いします。

次→[BASHとPowerShellのフィルタ処理まとめ⑤](https://qiita.com/HOBO0222/items/a7df9ad4ae28bd7f7050)