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

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

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

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

テスト用のフィルタするファイルは共通して以下3つを使用します。
①と全く同じ内容に戻しました。

```personal_infomation01.csv
***<空行>***
***<空行>***
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,女,1991/06/20,26,静岡県,AB
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
***<空行>***
***<空行>***
***<空行>***
```

```personal_infomation02.csv
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
2,河上里穂,カワカミリホ,女,1969/09/21,48,高知県,B
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
```

```personal_infomation03.csv
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```

ではぽんぽんとフィルタ紹介と比較をしていきます。

##【文字列指定による行削除】

###◆BASHの場合
特定文字列を含んだ行を削除する方法です。まずBASHはsedとgrepを使用してワンライナーで対応できます。今回はpersonal_infomation03.csvの真ん中の行(川名さんの行)を削除します。

```BASH:BASH_sedによる文字列指定の行削除
[root@centos74 log]# cat personal_infomation03.csv | sed '/川名/d'
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
sed '/string/d'で意図した通りに行削除できます。変換するわけではないのでsはいりません。最後にdeleteのdを指定すれば行削除してくれます。

```BASH:BASH_grepによる文字列指定の行削除
[root@centos74 log]# cat personal_infomation03.csv | grep -v 川名
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
grepは-vオプションで指定した文字列を含んだ行を検索対象から外してくれます。結果行削除したデータを取得できます。

###◆PowerShellの場合
PowerShellで特定文字列を含んだ行を削除する場合はWhere-Objectを使用します。まるでSQLクエリのWhereのように使用することができる、オブジェクトをフィルタするためのコマンドレットです。構文は以下の通りです。

```POWERSHELL:PowerShellによる文字列指定の削除
PS C:\Tools\logs> cat .\personal_infomation03.csv | Where-Object { $_ -notlike "*川名*" }
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
-notlikeは「ワイルドカードを使用した不一致」を意味します。ここでは「"川名"という文字列が含まれない行を指定」しています。結果は以上の通りに出力されます。

##【行指定による置換処理】

###◆BASHの場合
そのファイルの中で処理させる行を指定して置換します。これまで述べてきた例では、特別に行の指定をしていなかったため、すべての行を対象に処理を行っていました(sedのg抜きは少し違いますが)。しかし今回は、ファイルの何行目から何行目までを指定して処理する方法を紹介します。

今回3つのファイルをしっかり結合したファイルをpersonal_infomation04.csvとして使います。行数多めの方がわかりやすいので。16行です。全ての行に行番号を便宜上入れています(vimの:set nuであとで確認して追記してるだけです)。

```BASH:personal_infomation04.csv
[root@centos74 log]# cat personal_infomation*.csv | sed -e '/^$/d'
      1 連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
      2 1,海野華音,ウミノカノン,女,1991/06/20,26,静岡県,AB
      3 2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
      4 3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
      5 4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
      6 5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
      7 連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
      8 1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
      9 2,河上里穂,カワカミリホ,女,1969/09/21,48,高知県,B
     10 3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
     11 4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
     12 5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
     13 連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
     14 1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
     15 2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
     16 3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```

BASHはいつも通りsedで行います。2行目～15行目までの"女"を"男"に置換します。以下の通りです。

```BASH:BASH行指定による置換処理
[root@centos74 log]# cat personal_infomation04.csv | sed '2,15s/女/男/g'
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,男,1991/06/20,26,静岡県,AB
2,有馬花菜,アリマハナ,男,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,男,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,男,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,男,1979/07/04,38,静岡県,O
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
2,河上里穂,カワカミリホ,男,1969/09/21,48,高知県,B
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,男,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
「2,15」の部分で2～15行目を指定してその後の置換処理を行っています。ちなみに最後の行まで処理させたい時は明確な行数がわかっていなくても「2,$」を指定すれば処理されます。ダブルクォートで
囲った時だけは「2,\$」でエスケープすることは忘れないようにしてください。sedは便利です。

###◆PowerShellの場合
問題はPowerShellで、「行番号を範囲指定して置換する」というコマンドレットはありません(たぶん...)。そもそもPowerShellはオブジェクトなので「行」という概念がそもそもなく、通常は「インデックス番号」でいわゆる「行」を指定するような方法を取るしかありません。色々と試行錯誤しましたが私は以下のコマンドでワンライナーで処理しました。

```POWERSHELL:PowerShell行指定による置換処理
PS C:\Tools\logs> for ($i=0;$i -le 15;$i++) { if ( "$i" -match "^[1-9]$|^1[0-4]$" ) { (Get-Content .\personal_infomation04.csv).Replace("女","男")[$i] } else { (Get-Content .\personal_infomation04.csv)[$i] } }
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,男,1991/06/20,26,静岡県,AB
2,有馬花菜,アリマハナ,男,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,男,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,男,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,男,1979/07/04,38,静岡県,O
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
2,河上里穂,カワカミリホ,男,1969/09/21,48,高知県,B
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,男,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
for文とif文を使用して、分岐処理判定に正規表現を使っています。無理やりワンライナーにしたので見づらいと思いますので改行した状態を以下に記載します。

```POWERSHELL:PowerShell行指定による置換処理(構文01)
for ($i=0;$i -le 15;$i++) { 
  if ( "$i" -match "^[1-9]$|^1[0-4]$" ) { 
(Get-Content .\personal_infomation04.csv).Replace("女","男")[$i] 
  } else { 
(Get-Content .\personal_infomation04.csv)[$i] 
  } 
}
```
$iにインデックス番号を格納し、一行ずつfor文で読み込みながら、範囲内のインデックス番号は"女"から"男"へ置換を行い、そうでない場合はそのまま出力するという処理をしています。インデックス番号は0から始まるため「行数-1」で表す必要がある都合上、0から最後のインデックス番号(15)までループする判定に設定しています。

あとは、正規表現で「1~9もしくは10~14」のインデックス番号をif文の分岐処理で置換するようにしています。それ以外(ここだと0と15)のインデックス番号は置換せずそのまま出力しています。

これでとりあえず意図した通りの置換処理はできましたが、
ただ、これは実は手を抜いており、最後のインデックス番号(15)はテキストの容量が膨大な場合、数えるのはめんどくさくなるはずです。ということで、以下の通りの構文で最後のインデックス番号を変数に格納するべきですので、ワンライナーはやめたほうがよいです。

```POWERSHELL:PowerShell行指定による置換処理(構文)
$Length=(Get-Content .\personal_infomation04.csv).Length - 1 #行数から-1する
for ($i=0;$i -le $Length;$i++) { #ファイルの最初から最後の行まで1行ずつループ
  if ( "$i" -match "^[1-9]$|^1[0-4]$" ) {  #「1~9もしくは10~14」がマッチするか真偽判定
(Get-Content .\personal_infomation04.csv).Replace("女","男")[$i]  #Trueならば置換処理
  } else { 
(Get-Content .\personal_infomation04.csv)[$i]  #Falseならば置換処理せず
  } 
}
```
③までやってきましたが、初めてワンライナーを諦めました。なにか良い方法はないもんかな...。知ってる方いたらぜひ教えてください。sed関数を自前で作っちゃうくらいしか思い浮かばないです。このようにPowerShellは「インデックス番号の範囲指定」がコマンドレットにないため、不得手ということでしょうか。

##【行の削除及び抽出】
行の先頭から何行分、最後尾から何行分、指定した行番号だけ削除の方法を紹介・比較します。

###◆BASHの場合
まずはBASHです。先頭、最後尾、行番号指定ずつ記載します。

先頭から何行分かを指定して抽出する場合はheadを使います。

```BASH:BASH先頭から何行分か指定
[root@centos74 log]# head -6 personal_infomation04.csv
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,女,1991/06/20,26,静岡県,AB
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
```
-6で6行分だけ抽出するように指定しています。headだけで行数指定をしない場合は10行抽出するのがデフォルトになっています。

最後尾から何行分かを指定して抽出する場合はtailを使います。

```BASH:BASH最後尾から何行分かを指定
[root@centos74 log]# tail -4 personal_infomation04.csv
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
headと同じ仕様です。-3で3行分だけ抽出するように指定しています。同じくデフォルトは10行抽出するようになっています。

行番号を指定しての削除はsedのdを使います。

```bash:BASH行番号を指定して削除01
[root@centos74 log]# cat personal_infomation04.csv | sed -e '1,12d'
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```

ほんとにsedが好きになりそうです。次いきます。

###◆PowerShellの場合
次はPowerShellの先頭、最後尾、行番号指定を記載します。

先頭はSelect-Objectの-Firstを使用します。

```POWERSHELL:PowerShell先頭から何行分か指定
PS C:\Tools\logs> Get-Content .\personal_infomation04.csv | Select-Object -First 6
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,女,1991/06/20,26,静岡県,AB
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
```
ちなみにGet-Contentに-TotalCountをつけても同じ結果が返ってきます。

```POWERSHELL:PowerShell先頭から何行分か指定
PS C:\Tools\logs> Get-Content .\personal_infomation04.csv -TotalCount 6
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,女,1991/06/20,26,静岡県,AB
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
```

最後尾はSelect-Objectの-Lastを使用します。

```POWERSHELL:PowerShell最後尾から何行分か指定
PS C:\Tools\logs> Get-Content .\personal_infomation04.csv | Select-Object -Last 4
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```

Get-Contentに-tailをつけて同じ結果が返ってきます。

```POWERSHELL:PowerShell最後尾から何行分か指定
PS C:\Tools\logs> Get-Content .\personal_infomation04.csv -Tail 4
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```

そして「行範囲指定」もとい「インデックス番号指定」を指定しての削除ですが、正直ワンライナーでの方法が思い浮かびません。ただGet-Contentはインデックス番号指定での「表示」が可能です。指定したインデックス番号部分のオブジェクトを表示できるようになっています。

```POWERSHELL:PowerShell行番号を指定して表示01(12から15のインデックス番号を表示)
PS C:\Tools\logs> (Get-Content .\personal_infomation04.csv)[12..15]
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```

行表示はコマンドレット対応できますがワンライナーでの行削除が私には思い浮かびません。

```POWERSHELL:PowerShell行番号を指定して削徐01
PS C:\Tools\logs> $Length=(Get-Content .\personal_infomation04.csv).Length - 1 #行数から-1する
PS C:\Tools\logs> for ($i=0;$i -le $Length;$i++) { #ファイルの最初から最後の行まで1行ずつループ
>>   if ( "$i" -notmatch "^[0-9]$|^1[0-1]$" ) {  #「0~9もしくは10~11」がノットマッチするか 真偽判定
>> (Get-Content .\personal_infomation04.csv)[$i]  #真ならば特定行出力
>>   }
>> }
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
↑の構文で正規表現の数字を修正すればインデックス番号指定での行削除は可能です。でもやっぱりワンライナーがいいですねえ。

##【コメント行の削除】
BASHもPowerShellもコメントは「#」を利用します。#が記載されている行を指定してコメント行を削除する方法です。

なお本章のために以下のようにテストファイルを編集しています。

```BASH:personal_infomation04.csv
[root@centos74 log]# cat personal_infomation04.csv
###これはコメント行です。
#コメントしています。小見出しです。
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,女,1991/06/20,26,静岡県,AB #コメントしています。
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
2,河上里穂,カワカミリホ,女,1969/09/21,48,高知県,B
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```

###◆BASHの場合
いつも通りsedで消します。以下の通りです。

```BASH:BASHコメント行の削除
[root@centos74 log]# cat personal_infomation04.csv | sed '/#.*/d'
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
2,河上里穂,カワカミリホ,女,1969/09/21,48,高知県,B
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
ちなみに先頭に#がある行だけ消すなら以下でオッケーです。

```BASH:BASH先頭に#がある場合
cat personal_infomation04.csv | sed '/^#/d'
```
sedが便利すぎるので、今回は一度だけsedがないBASHを想定してコメント行を削除してみます。以下の方法で消せます。

```BASH:BASH、sedなしでのコメント行削除
[root@centos74 log]# cat personal_infomation04.csv |
> while read LINE
> do
> case $LINE in
> *#* ) continue
> ;;
> * ) echo $LINE
> ;;
> esac
> done
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,女,1991/06/20,26,静岡県,AB
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
2,河上里穂,カワカミリホ,女,1969/09/21,48,高知県,B
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```

While read LINEで一行一行case文へ放り込んで「#」が入っているものはスルー(continue)、入ってないものをechoで表示し続ける処理ですね。sedがないとこうなります。先頭に#がある場合はcase分の「*#*」の部分を「/#*」に変えればオッケーです。/はエスケープのため入力必須です。エスケープしないとコメントだと認識しちゃいます。

###◆PowerShellの場合
PowerShellの場合はWhere-Objectで-notlike演算子を使用してコメント行を削除します。

```POWERSHELL:PowerShellコメント行を削除
PS C:\Tools\logs> Get-Content .\personal_infomation04.csv | Where-Object { $_ -notlike "*#*" }
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
2,河上里穂,カワカミリホ,女,1969/09/21,48,高知県,B
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```

-like、-notlike演算子はワイルドカード系ですので、*で囲って行全体を消すようにしています。

##【キーワードによる行の範囲指定】
キーワードによって範囲指定した部分のみ表示させます。

###◆BASHの場合
5行目の米沢さんから9行目の河上さんまでをキーワード指定して表示させます。

```BASH:BASHキーワードによる行の指定
[root@centos74 log]# cat personal_infomation04.csv | sed -n '/米沢/,/河上/p'
4,米沢里桜,ヨネザワリオ,女,1989/05/05,28,長崎県,A
5,楠本綾菜,クスモトアヤナ,女,1979/07/04,38,静岡県,O
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
2,河上里穂,カワカミリホ,女,1969/09/21,48,高知県,B
```
-n オプションを追加してpフラグを付けると、文字列がマッチした行のみを出力するようになります。

また、逆に米沢さんと河上さん間の行以外を表示したい場合は以下の通りです。

```BASH:BASHキーワードによる行以外の指定
[root@centos74 log]# cat personal_infomation04.csv | sed -e '/米沢/,/河上/d'
###これはコメント行です。
#コメントしています。小見出しです。
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,女,1991/06/20,26,静岡県,AB #コメントしています。
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
つまり、-e～dで指定範囲を削除、-n～pで範囲指定を表示と覚えてくれればいいです。
ただしキーワードが意図しないところにある場合もありこの構文はデリケートです。慎重を期すためキーワードに^や$を付与するなどして厳密に指定するようにしましょう。

最後の補足ですがエクスクラメーションマーク(! )を処理コマンド(pやdといった表示や削除を指示するフラグ部分)の直前に置くことで逆の操作を指定できるようになります。

```BASH:BASHキーワードによる行以外の指定
[root@centos74 log]# cat personal_infomation04.csv | sed -n '/米沢/,/河上/!p'
###これはコメント行です。
#コメントしています。小見出しです。
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,海野華音,ウミノカノン,女,1991/06/20,26,静岡県,AB #コメントしています。
2,有馬花菜,アリマハナ,女,1989/12/25,28,栃木県,A
3,小柳有紀,コヤナギユキ,女,1998/04/04,20,福井県,O
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,早川哲二,ハヤカワテツジ,男,1996/07/11,21,愛知県,A
2,川名来未,カワナクルミ,女,1976/05/15,41,大分県,O
3,村田蒼衣,ムラタアオイ,女,1967/03/27,51,奈良県,A
```
pの直前にエクスクラメーションマークをつけるとdと同じ結果が返ってきます。
なんかsed押しの記事になってきました。

###◆PowerShellの場合

(ワンライナーでは)無理じゃないかなあ...。
これはすみません。出力できる構文を考えるのに時間がかかりそうなので、一旦なしにします。申し訳ないです。Select-StringやWhere-Objectなどでどうにか方法あるかなと探しましたが、簡単には見つかりませんでしたね。。

PowerShellについて少し弁明させてほしいのですが、そもそもPowerShellはオブジェクト前提なので、パイプで渡すのは文字列ではなくてオブジェクトだよという作りになっているのを、今回比較して感じました。インデックス番号なら範囲指定が簡単にできるのに、文字列での範囲指定はコマンドレットや構文にないというのがその証左になるかなと(あったら恥ずかしいですが、、神PowerShellerの方コメントお待ちしています)。

この記事で「sedすげえ」と驚いた方がいるかもしれませんが、sedがあるBASHは、テキストフィルタ処理部分でいえばPowerShellを上回ります。そもそもテキストフィルタ処理前提で比較してしまって、PowerShellには申し訳ないと思い始めています。

むしろ逆転の発想で、

**ワンライナー処理に特化しているBASHと比較対象できちゃう
PowerShellは逆にすごいんです。普通比較されません。**


##【まとめ＆所感＆次】
なんか結論みたいになってしまいましたが、まだこの記事シリーズは続けます。テンポ良く作れるので面白いです。ワンライナーでできないと時間かかりますが...。

またいずれまとめますが、ここまでで比較した時の所感を少し書きます。
BASHとPowerShell比較ポイントの一つとなるでしょうが、「シンプルさ」と「機能の豊富さ」のどちらを重視するのかというところがこの二つの言語は度々見て取れます。

「シンプルさ」でいえばBASHは他の追随を許さないのではないでしょうか。
最低限の機能から汎用性の高い処理を選択できるため可読性が高く学習効率も良いです。その分、外部連携や配列めいた処理など、複雑な処理をせざるを得なくなると対応力がなくめんどくさくなりますが、それは追々でてくるのかなと思います。

「機能の豊富さ」でいえばPowerShellですね。
コマンドレットやBATCHから引き継ぐコマンド群、オブジェクト処理、コア関数利用(特に数学)、文字列処理、配列処理、.NET FRAMEWORKとの連携、WinRMサービスとの連携、各種Windows製品、その他外部サービスとの連携などなど。対応範囲の幅でいえばBASHに勝ると思います。ただその分だけ個々によって文法も異なるでしょうから可読性や学習効率も低い(BASHと比べたらですが。個人的にはわかりやすい部類です)と感じています。

ちなみにここで紹介するのはどうかと思いますがPowerShellは既定のエイリアス登録が豊富で、BASHのcatやls、mvなどなどを同じように使えてしまいます。

```POWERSHELL:PowerShell、catとGet-Content
PS C:\Tools\logs> cat .\personal_infomation02.csv
連番,氏名,氏名（カタカナ）,性別,生年月日,年齢,出身地,血液型
1,島田清蔵,シマダセイゾウ,男,1982/08/25,35,鳥取県,A
2,河上里穂,カワカミリホ,女,1969/09/21,48,高知県,B
3,三輪博昭,ミワヒロアキ,男,1975/05/25,42,福岡県,O
4,本郷勝次,ホンゴウカツジ,男,1966/02/17,52,東京都,O
5,横川金次,ヨコカワキンジ,男,1985/01/14,33,徳島県,AB
```

catを打つとGet-Contentがエイリアスで使用されるようになっています。
たぶんBASH使いの方々が入りやすいようにとのMSの配慮なんでしょうが、エイリアスを多用する分だけ個々での書き方に幅が出てしまい、可読性は落ちます。%と?は最初「え？」ってなりました。

しかしながら、BASHerの方々はとっつきやすいと思うので、ぜひこれを機にPowerShellを学習してみてはどうでしょうか。PowerSheller仲間欲しいです。


以上です。
次回④も記事にする予定で今度は「列」フィルタに焦点を当てようかなと思います。BASHはcutやawkが出てきて、PowerShellはforeachですかね。マイペースでまとめていきます。

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

次の記事
[BASHとPowerShellのフィルタ処理まとめ④](https://qiita.com/HOBO0222/items/456292b2adc7c62f3c80)