ひゃまだのblog

ひゃまだ(id:hymd3a)の趣味のブログ

Bashでカンマ区切りのCSVファイルを扱う

(2024-04-26 初稿)

こちらのblogは、ちょっと久しぶりにアップする。

最近、株の乱高下が激しいので、楽天証券保有株一覧を見ているのだけど、不要な列があったり、ちょっと特殊な集計をしたかったりするので、自分で表計算ソフトにちまちまとコピぺしていたんだけど、いい加減アホらしくなったので、python等で書けばちゃんと表計算ソフトのファイルを作成することができるが、テキストファイルが好きなのでBashの勉強のためにスクリプトを作ってみた。

ありがたいことに、楽天証券からは保有株一覧のファイルをダウンロードできるので、カンマ区切りのCSVファイルをダウンロードして利用することにした。

楽天証券CSVだけならば、変な文字列も無いのですぐにできたのだが、もう少し汎用的なCSVファイルでも、動作するようにしてみた。

まずは、カンマ区切りのCSVだからとIFSのデリミタをカンマ(,)してset関数で分割を考えたが、数字の1000単位のカンマがあり、あえなく撃沈。試行錯誤の結果、結局はダブルクォートを目印(デリミタ)にして、データを区切ることにした。

入力行から複数の値を取得するのは、read関数を使うと便利。

簡単なサンプルスクリプト

#!/usr/bin/env bash
# csv-split
# csvファイルの値を配列に
# 2024-04-25 ver0.01 start

test_line='"あ,い! う","a! b,c","123,456,789", "4,321"'

IFS='"' read  v1 v2 v3 v4 v5 v6 v7 v8 <<< "$test_line"
echo 'v1=' \""$v1"\"         #⇒ v1= ""
echo 'v2=' \""$v2"\"         # ⇒ v2= "あ,い! う"
echo 'v3=' \""$v3"\"         # ⇒ v3= ","
echo 'v4=' \""$v4"\"         # ⇒ v4= "a! b,c"
echo 'v6=' \""$v6"\"         # ⇒ v6=  "123,456,789"
echo 'v8=' \""$v8"\"         # ⇒ v8=  "4,321"
echo 'need_value=' \""$v4"\",\""$v8"\"  # ⇒ need_value= "a! b,c","4,321"

ダブルクォートをデリミタにしたことにより、最初の変数(v1)に空白、次(v2)に値、その次(v3)がカンマ、と偶数番目の変数に値が読み込まれる。

また、任意の値を選択して取得することも可能。

固定化したCSVファイルなら良いが、もう少し汎用性をもたせて読み込む値(変数)の数が変わる場合は、以下のとおりreadに-aオプションを付けて配列として読み込む方が都合が良い。

IFS='"' read -a csv_ary <<< "$test_line"
echo 'csv_ary[0]=' \""${csv_ary[0]}"\"  # ⇒ csv_ary[0]= ""
echo 'csv_ary[1]=' \""${csv_ary[1]}"\"  # ⇒ csv_ary[1]= "あ,い! う"
echo 'csv_ary[2]=' \""${csv_ary[2]}"\"  # ⇒ csv_ary[2]= ","
echo 'csv_ary[3]=' \""${csv_ary[3]}"\"  # ⇒ csv_ary[3]= "a! b,c"
(以下、省略)

配列の添字が、0から始まっているので、今度は奇数番目に値が入っている。

1番目⇒1、2番目⇒3、3番目⇒5の添字を指定するが、n番目のデータは2n-1の添字を使えば値を取得できる。

また、値の順番を変更できるようにorderを指定できるようにし、かつ、関数にしたものが以下のとおり。

#!/usr/bin/env bash
# csv-split
# csvファイルを配列に分割する
# 2024-04-25 ver0.01 start
test_line='"あ,い! う","a! b,c","123,456,789", "4,321"'

get_csv_value() {
  # csvの値を指定する順番で出力
  local rslt=""
  IFS='"' read -a csv_ary <<< "$1"
  #echo 'csv_ary=' "${csv_ary[@]}"
  IFS=, read -a order_ary <<< $2
  #echo 'order_ary=' "${order_ary[@]}"
  for ((i=0; i<${#order_ary[@]}; i++)); do
    j=$((${order_ary[$i]} * 2 -1))      # n番目は配列の2n-1番目
    rslt+=\""${csv_ary[$j]}"\",
  done
  echo ${rslt%,}     # 最後の,の削除

order="2,1,3"         # 順番の指定
putline=$(get_csv_value "$test_line" "$order")
echo $putline

実行権を与えてから、実行すると以下のとおり。

# csv-split
"a! b,c","あ,い! う","123,456,789"

今回、read関数で配列にすることが便利なことがわかった。

どなたかの参考になれば幸い。

関連ページ