◾️はじめに
S3上のCSVヘッダーを比較する必要が出てきて、 はじめは、目視でやっていたのだが、面倒臭い+ミスが出そうだが 忙しく焦っている時にシェル書いても余計イライラしそうだなっと思っていたが、 ふっとAI(Copilot)に書いてもらおうと思い、書いてもらった中で commコマンドが出てきたのでメモ。 (業務実際に使ったものと違うスクリプトができたが、むしろ、以下の方が良さそう)
【1】commコマンド
* 2つのファイルを比較し、以下をそれぞれ出力する + FILE1のみに存在する値 + FILE2のみに存在する値 + 両方に存在する値 cf. comm = common? compare??
1)基本構文
comm [options] file1 file2
| オプション | 説明 |
|---|---|
| -1 | 1列目を表示しない(file1だけの行を非表示) |
| -2 | 2列目を表示しない(file2だけの行を非表示) |
| -3 | 3列目を表示しない(共通する行を非表示) |
2)例
# 例1 $ comm file1.txt file2.txt apple banana grape orange # 例2:file1にしかない行を表示 $ comm -23 file1.txt file2.txt apple # 例3:両方に存在するものが表示 $ comm -1 -2 file1.txt file2.txt banana orange
file1.txt
apple banana orange
file2.txt
banana grape orange
【2】サンプル
例1:S3上のCSVヘッダーを比較する
#!/bin/bash # Compare the header rows of two CSV files on S3 without downloading the whole files. # # Usage: # ./compare_s3_csv_headers_no_download.sh s3://bucket1/file1.csv s3://bucket2/file2.csv [range_size] # # Examples: # ./compare_s3_csv_headers_no_download.sh s3://my-bucket/foo.csv s3://my-bucket/bar.csv # ./compare_s3_csv_headers_no_download.sh s3://my-bucket/foo.csv s3://my-bucket/bar.csv 400000 # # [range_size] (optional): The number of bytes to fetch from the head of each file. Default is 300000. if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then echo "Usage: $0 s3://bucket1/file1.csv s3://bucket2/file2.csv [range_size]" 1>&2 exit 1 fi FILE1="$1" FILE2="$2" RANGE_SIZE="${3:-300000}" TMPDIR=$(mktemp -d) trap "rm -rf $TMPDIR" EXIT function get_header() { S3PATH="$1" LOCALFILE="$2" RANGE="$3" BUCKET=$(echo "$S3PATH" | cut -d/ -f3) KEY=$(echo "$S3PATH" | cut -d/ -f4-) aws s3api get-object --bucket "$BUCKET" --key "$KEY" --range "bytes=0-$RANGE" "$LOCALFILE" >/dev/null 2>&1 head -n 1 "$LOCALFILE" } HEADER1=$(get_header "$FILE1" "$TMPDIR/file1.head" "$RANGE_SIZE") HEADER2=$(get_header "$FILE2" "$TMPDIR/file2.head" "$RANGE_SIZE") # Remove double quotes, make lower-case, split by comma, sort echo "$HEADER1" | tr -d '"' | tr ',' '\n' | tr '[:upper:]' '[:lower:]' | sort > "$TMPDIR/header1.list" echo "$HEADER2" | tr -d '"' | tr ',' '\n' | tr '[:upper:]' '[:lower:]' | sort > "$TMPDIR/header2.list" DISAPPEARED=$(comm -23 "$TMPDIR/header1.list" "$TMPDIR/header2.list") APPEARED=$(comm -13 "$TMPDIR/header1.list" "$TMPDIR/header2.list") echo "===== Columns disappeared (present in $FILE1 only) =====" if [ -n "$DISAPPEARED" ]; then echo "$DISAPPEARED" else echo "None" fi echo echo ">>>>> Columns ADDED in $FILE2 (present only in $FILE2) <<<<<" if [ -n "$APPEARED" ]; then echo "$APPEARED" else echo "None" fi
1)処理の流れ
[1] aws s3api get-objectコマンドでファイルの一部を取得 [2] 一時ファイルとして退避しておく [3] 一行目を取得し、ヘッダー部を抽出 [4] [1]~[3] をそれぞれ2ファイルに行う [5] それぞれのヘッダー部に対して、以下を行う * ダブルクォート(")を削除する * カンマ区切りをパース * 小文字に統一 * ソート [6] commコマンドを使って、比較する
2)使用上の注意
「head: cannot open 'tmp/tmp.xxx/file1.head' for reading: No such file or directory」が表示
aws s3api get-object の失敗の可能性が高いので、 第3引数のrangeを例えば「500000」のように増やして試す
3)使用したその他コマンド
trapコマンド
* シグナルを受け取った際の動作を設定、表示するコマンド
https://atmarkit.itmedia.co.jp/ait/articles/1907/25/news010.html
cutコマンド
* 文字列を切り出す => 詳細は以下の関連記事を参照のこと
シェル ~ 文字列抽出あれこれ ~
https://dk521123.hatenablog.com/entry/2021/08/03/160901
trコマンド
* テキストファイルの文字を置換する => 詳細は以下の関連記事を参照のこと
ファイル内容を操作するコマンド
https://dk521123.hatenablog.com/entry/2023/07/26/000000
4)Tips
* 大文字小文字を区別しない(tr '[:upper:]' '[:lower:]'で小文字化) * 順不同(sortで比較)
関連記事
シェル について ~入門編~
https://dk521123.hatenablog.com/entry/2014/10/23/005406
ファイル内容を操作するコマンド
https://dk521123.hatenablog.com/entry/2023/07/26/000000
大きいファイルを扱う際のコマンド
https://dk521123.hatenablog.com/entry/2020/06/12/000000
ファイルの内容を表示するコマンド
https://dk521123.hatenablog.com/entry/2016/08/08/235934
Linux ~ パフォーマンスに関わるコマンド ~
https://dk521123.hatenablog.com/entry/2015/02/21/112554
制御文字を扱うためのコマンド
https://dk521123.hatenablog.com/entry/2024/12/03/003405
Linuxコマンドで集合演算するには ~ sort / uniq ~
https://dk521123.hatenablog.com/entry/2024/05/11/011744
シェル ~ 文字列抽出あれこれ ~
https://dk521123.hatenablog.com/entry/2021/08/03/160901
awk コマンド
https://dk521123.hatenablog.com/entry/2019/11/22/223043
sedコマンド
https://dk521123.hatenablog.com/entry/2019/11/23/101625
grep / egrep / fgrepコマンド
https://dk521123.hatenablog.com/entry/2017/08/06/213100
Linuxコマンドで集合演算するには ~ sort / uniq ~
https://dk521123.hatenablog.com/entry/2024/05/11/011744