1. 概要

Linuxでは、多くの場合、CSV形式のファイルまたはテキストを処理する必要があります。 このクイックチュートリアルでは、CSVデータを分割して結果をBashに保存する方法について説明します。

2. 問題の紹介

いつものように、例を通して問題を理解します。 まず、入力を見てみましょう。

$ echo $INPUT 
Kotlin Corroutines,Java Stream API,Ruby On Rails,Other Language Features

上記の出力が示すように、変数 $INPUTに1行のコンマ区切り値が格納されています。 鋭い目は、の値に入力文字列にスペースが含まれていることに気付いたかもしれません。

私たちの目標は、Bashの $INPUT変数の値を解析することです。

ご覧のとおり、入力は1行の入力です。 まず、単一行入力シナリオを解決する方法に焦点を当てます。

最後に、ソリューションを拡張して、複数行の入力の場合を解決します。

3. awk を使ってみませんか?

awkはLinuxコマンドラインの強力なテキスト処理ツールです。 特に、CSVなどの列ベースのデータの処理に優れています。 したがって、たとえば、 awk を使用すると、$INPUTの値を簡単に解析できます。

$ awk -F',' '{ for( i=1; i<=NF; i++ ) print $i }' <<<"$INPUT"
Kotlin Corroutines
Java Stream API
Ruby On Rails
Other Language Features

上記のawkコマンドで見たように、入力テキストは正しく解析されています。

ただし、解析された値はawkコマンドに存在することに注意してください。 入力テキストを別の形式に変換したいだけの場合は、awkが適切な選択肢の1つです。 ただし、たとえば、解析した各値をシェル変数に保存してさらに使用する場合は、シェルスクリプトで保存する必要があります。

4. データを前処理してから配列に格納する

awkは解析された結果をシェルに直接提供できないことを説明しました。 ただし、CSVデータを解析して複数行形式に変換できるため、 awk を使用して入力を変換し、複数行の値を配列に保存できます。

Bashがバージョン4以降の場合、組み込みのreadarrayコマンドを使用してawkコマンドの出力を読み取ることができます

$ readarray -t the_array < <(awk -F',' '{ for( i=1; i<=NF; i++ ) print $i }' <<<"$INPUT")
$ declare -p the_array
declare -a the_array=([0]="Kotlin Corroutines" [1]="Java Stream API" [2]="Ruby On Rails" [3]="Other Language Features")

を使用します readarray 前処理されたデータを配列変数に格納するコマンド the_array。 また、 宣言するコマンドの出力は、 the_array 配列には期待されるデータが含まれています。

readarray コマンドは便利ですが、古いバージョンのBashでは使用できません。

ただし、readコマンドはすべてのBashバージョンで使用できます。 したがって、 IFS変数を調整し、readコマンドを使用して前処理されたデータを配列変数に格納できます。

$ IFS=$'\n' read -r -d '' -a the_array2 < <(awk -F',' '{ for( i=1; i<=NF; i++ ) print $i }' <<<"$INPUT")
$ declare -p the_array2
declare -a the_array2=([0]="Kotlin Corroutines" [1]="Java Stream API" [2]="Ruby On Rails" [3]="Other Language Features")

IFS 変数の変更は、直後の読み取りステートメントの変数のみを設定することに注意してください。 現在のシェル環境にまったく干渉しません。

5. IFS =、の設定

前の例では、 IFS = $’\ n’を設定し、readコマンドを使用して前処理されたデータを配列に格納しました。 または、 IFS =、(カンマ)を設定して、生の入力から各値を直接読み取ることもできます。

たとえば、フィールドの数がわかっている場合は、各値を読み取って個々のシェル変数に割り当てることができます。

$ cat four_fields.sh
#!/bin/bash
INPUT="Kotlin Corroutines,Java Stream API,Ruby On Rails,Other Language Features"

IFS=, 
read KOTLIN JAVA RUBY OTHER <<<$INPUT

#Verify the result
echo 'Var $KOTLIN has the value:'$KOTLIN
echo 'Var $JAVA has the value:'$JAVA
echo 'Var $RUBY has the value:'$RUBY
echo 'Var $OTHER has the value:'$OTHER

デモンストレーションを簡単にするために、すべてをfour_fields.shという名前のシェルスクリプトに入れます。 この小さなスクリプトでは、 IFS = ,. を設定します。次に、ご存知のように、入力には4つのフィールドがあります。 次に、各値を個々の変数に割り当てます。 最後に、4つの変数の値を出力して、入力が正しく解析されるかどうかを確認します。

それでは、スクリプトを実行してみましょう。

$ ./four_fields.sh
Var $KOTLIN has the value:Kotlin Corroutines
Var $JAVA has the value:Java Stream API
Var $RUBY has the value:Ruby On Rails
Var $OTHER has the value:Other Language Features

どうやら、このアプローチは期待どおりに機能します。

フィールドの数がわからない場合でも、IFS =を設定し、readを使用して値を配列に格納できます

$ cat ./fields_in_array.sh
#!/bin/bash
INPUT="Kotlin Corroutines,Java Stream API,Ruby On Rails,Other Language Features"
IFS=,
read line <<<$INPUT
FIELDS=( $line )

#verify the result
declare -p FIELDS

$ ./fields_in_array.sh
declare -a FIELDS=([0]="Kotlin Corroutines" [1]="Java Stream API" [2]="Ruby On Rails" [3]="Other Language Features")

上記の例が示すように、 read コマンドを使用して、 IFS = ,. を設定することにより、CSVデータを解析し、値を配列に格納できます。

6. 入力が複数行のCSVファイルの場合

これまで、1行のCSV入力を解析して保存する方法について説明してきました。 複数行のCSVファイルを処理する必要がある場合は、whileループを使用してソリューションを拡張できます。

例を見てみましょう:

$ cat input.csv
Kotlin Corroutines,Java Stream API,Ruby On Rails,Other Language Features
Kotlin Corroutines 2,Java Stream API 2,Ruby On Rails 2,Other Language Features 2
Kotlin Corroutines 3,Java Stream API 3,Ruby On Rails 3,Other Language Features 3

これで、3行を含むCSVファイルinput.csvができました。

whilereadを使用してすべての行を解析する方法を見てみましょう。

$ cat ./fields_in_file.sh
#!/bin/bash
IFS=,
while read line; do
  FIELDS=( $line )

  # using the array to do further operations
  #....
  declare -p FIELDS

done < input.csv

$ ./fields_in_file.sh
declare -a FIELDS=([0]="Kotlin Corroutines" [1]="Java Stream API" [2]="Ruby On Rails" [3]="Other Language Features")
declare -a FIELDS=([0]="Kotlin Corroutines 2" [1]="Java Stream API 2" [2]="Ruby On Rails 2" [3]="Other Language Features 2")
declare -a FIELDS=([0]="Kotlin Corroutines 3" [1]="Java Stream API 3" [2]="Ruby On Rails 3" [3]="Other Language Features 3")

ご覧のとおり、スクリプト fields_in_file.sh は、前のfield_in_array.shwhileループで拡張します。

行を解析し、配列 FIELDS に値を格納した後、describeコマンドを使用してFIELDSの内容を出力します。 ただし、配列を使用して、現実の世界でいくつかの意味のあるタスクを実行できます。

スクリプトを実行すると、期待される出力が出力されることがわかります。

7. 結論

この記事では、例を通じてBashでのCSV形式のデータの解析について説明しました。