【Shell】シェル ~ CSV / TSVファイルの読み込み ~

■ はじめに

シェルで、ヘッダー付きのCSV / TSVファイルの読み込み
その一部を列を配列として扱いたいので、調べてメモっておく。

Memo

* CSV = Comma Separated Values(カンマ区切り)
* TSV = Tab Separated Values(タブ区切り)
* DSV = User-Defined Separated Values (ユーザ定義区切り)

目次

【1】CSVファイルを読み込み出力する
【2】ヘッダー(1行目)を除外して出力する
【3】TSVファイルを読み込む
【4】Tips
 1)ファイルの読み込み
 2)read / IFS
 3)カウントアップ
 4)sed

【1】CSVファイルを読み込み出力する

第一段階として、配列に入れて出力するだけのサンプルを作ってみた。

基本的な考え

* ファイルを標準入力として渡す
 => 以下の「【4】Tips の 1)ファイルの読み込み」を参照

* 上記をそのまま read コマンドによって配列として受け取る
 => 以下の「【4】Tips の 2)read / IFS」を参照

サンプル

sample_csv.sh

#!/bin/bash

parse_csv() {
# ${1} : CSV file (e.g. 'sample_csv.csv')
  readonly CSV_FILE="${1}"

  declare -a csv_data

  line_number=1
  while IFS=, read -a csv_data; do
    for value in "${csv_data[@]}";
    do
      echo "line_number = ${line_number}, value = ${value}"
    done

    # count up
    line_number=`expr $line_number + 1`
  done < "${CSV_FILE}"
}

# Call the function
parse_csv "sample_csv.csv"

sample_csv.csv

id,db_name,table_name
1,db1,sample_table1_1
2,db1,sample_table1_2
3,db2,sample_table2_1

出力結果

$ ./sample_csv.sh
line_number = 1, value = id
line_number = 1, value = db_name
line_number = 1, value = table_name
line_number = 2, value = 1
line_number = 2, value = db1
line_number = 2, value = sample_table1_1
line_number = 3, value = 2
line_number = 3, value = db1
line_number = 3, value = sample_table1_2
line_number = 4, value = 3
line_number = 4, value = db2
line_number = 4, value = sample_table2_1

【2】ヘッダー(1行目)を除外して出力する

ヘッダー部分を除外したいので調べてみたら
以下のサイトが見つかった

https://stackoverflow.com/questions/31911179/ignoring-first-line-column-header-while-reading-a-file-in-bash

sed を使って、ヘッダー部分を除外している。
(詳細は、以下の「【4】Tips の 4)sed」を参照)

サンプル

sample_csv.sh

#!/bin/bash

parse_csv_without_header() {
# ${1} : CSV file (e.g. 'sample_csv.csv')
  readonly CSV_FILE="${1}"

  sed 1d "${CSV_FILE}" | while IFS=, read -r id db_name table_name; do
    echo "id ${id} - db_name=${db_name} / table_name=${table_name}"
  done
}

parse_csv_without_header "sample_csv.csv"

出力結果

$ ./sample_csv.sh
id 1 - db_name=db1 / table_name=sample_table1_1
id 2 - db_name=db1 / table_name=sample_table1_2
id 3 - db_name=db2 / table_name=sample_table2_1

【3】TSVファイルを読み込む

サンプル

sample_tsv.sh

#!/bin/bash

parse_tsv_without_header() {
# ${1} : CSV file (e.g. 'sample_csv.csv')
  readonly CSV_FILE="${1}"

  sed 1d "${CSV_FILE}" | while IFS=$'\t' read -r id db_name table_name; do
    echo "id ${id} - db_name=${db_name} / table_name=${table_name}"
  done
}

parse_tsv_without_header "sample_tsv.csv"

sample_tsv.csv

id   db_name table_name
1   db1 sample_table1_1
2   db1 sample_table1_2
3   db2 sample_table2_1

出力結果

$ ./sample_tsv.sh
id 1 - db_name=db1 / table_name=sample_table1_1
id 2 - db_name=db1 / table_name=sample_table1_2
id 3 - db_name=db2 / table_name=sample_table2_1

【4】Tips

1)ファイルの読み込み

https://qiita.com/tag1216/items/7ce35b7c27d371165e56

# より抜粋

# ファイルの内容をコマンドの標準入力へ渡す
command < file

2)read / IFS

readコマンド

read [variable1, …]

* 標準入力から行を 1 行読み取り、
 その行を複数のフィールドに分割し、
 各 variable に割り当てます
* 環境変数 IFS を使用する

環境変数 IFS

* IFS = Internal Filed Separator (内部ファイル区切り文字)
 => 区切り文字を設定できる
 => 詳細は、以下の関連記事を参照のこと

https://dk521123.hatenablog.com/entry/2024/01/24/234634

3)カウントアップ

line_number=`expr $line_number + 1`

exprコマンド

* 計算式を評価するコマンド

https://atmarkit.itmedia.co.jp/ait/articles/1712/28/news019.html

# オプション 説明
1 -a 読み込んだ単語を配列にセットする
2 -r エスケープ文字を無効

https://atmarkit.itmedia.co.jp/ait/articles/1811/28/news003.html
https://linuxcommand.net/read/

4)sed

sed を使って、ヘッダー部分を除外している。
 => sed 1d : 1(行目) d (delete:削除)

なお、sedについは、以下の関連記事を参照のこと。

sedコマンド
https://dk521123.hatenablog.com/entry/2019/11/23/101625

参考文献

https://genzouw.com/entry/2019/02/06/081505/765/
https://qiita.com/SoarTec-lab/items/4475ba6de612fba3f163

関連記事

シェル ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2014/10/23/005406
シェル ~ 文字列抽出あれこれ ~
https://dk521123.hatenablog.com/entry/2021/08/03/160901
シェル ~ 環境変数 IFS ~
https://dk521123.hatenablog.com/entry/2024/01/24/234634
sedコマンド
https://dk521123.hatenablog.com/entry/2019/11/23/101625