aws cliでS3バケット内の全オブジェクトの情報を取得する

# はじめに
S3バッチオペレーションを試したいなーと思ったのですが、インプットに以下の形式のcsvが必要なようでした。

> CSV 形式のマニフェスト（バージョン ID なし）の例を以下に示します。
> Examplebucket,objectkey1
> Examplebucket,objectkey2
> Examplebucket,objectkey3
> Examplebucket,photos/jpgs/objectkey4
> Examplebucket,photos/jpgs/newjersey/objectkey5
> Examplebucket,object%20key%20with%20spaces
> 
> バージョン ID を含む CSV 形式のマニフェストの例は次のとおりです。
> Examplebucket,objectkey1,PZ9ibn9D5lP6p298B7S9_ceqx1n5EJ0p
> Examplebucket,objectkey2,YY_ouuAJByNW1LRBfFMfxMge7XQWxMBF
> Examplebucket,objectkey3,jbo9_jhdPEyB4RrmOxWS0kU0EoNrU_oI
> Examplebucket,photos/jpgs/objectkey4,6EqlikJJxLTsHsnbZbSRffn24_eh5Ny4
> Examplebucket,photos/jpgs/newjersey/objectkey5,imHf3FAiRsvBW_EHB8GOu.NHunHO1gVs
> Examplebucket,object%20key%20with%20spaces,9HkPvDaZY5MVbMhn6TMn1YTb5ArQAo3w

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/batch-ops-create-job.html#specify-batchjob-manifest

jqや ```--query``` オプションに苦手意識があったので、克服も兼ねて色々やってみました！

# リファレンス
aws cliのバージョンは2.5.2です（アプデしないと・・・）

```bash
$ aws --version
aws-cli/2.5.2 Python/3.9.11 
```

参考にしたドキュメントです

https://docs.aws.amazon.com/cli/latest/reference/s3api/list-objects-v2.html

https://docs.aws.amazon.com/cli/latest/reference/s3api/list-object-versions.html

# やってみた
## 準備

4つのオブジェクトが保存されているバケットを用意しました。

![cap1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3654730/527e4844-47b9-9542-8a66-6e52824d06a9.png)

## 特定のバケットのオブジェクトを全量取得する

### コマンド
```bash
aws s3api list-objects-v2 \
--bucket test-batch-ope-ito-log-a8f2ms
```

```--bucket```
　対象バケット名を指定します

### 実行結果
```bash
$ aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms
{
    "Contents": [
        {
            "Key": "cap.jpg",
            "LastModified": "2024-05-11T03:13:47+00:00",
            "ETag": "\"5ee32753dd30e0580d8075ff5a228921\"",
            "Size": 44575,
            "StorageClass": "STANDARD"
        },
        {
            "Key": "memo.txt",
            "LastModified": "2024-05-11T03:13:14+00:00",
            "ETag": "\"6436a6c961b90c90682e8fa52e03a6f2\"",
            "Size": 1161,
            "StorageClass": "STANDARD"
        },
        {
            "Key": "shot.jpg",
            "LastModified": "2024-05-11T03:32:50+00:00",
            "ETag": "\"cd4854823f017f45f391161fb53e6c28\"",
            "Size": 209678,
            "StorageClass": "STANDARD"
        },
        {
            "Key": "test.csv",
            "LastModified": "2024-05-11T03:14:28+00:00",
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
            "Size": 0,
            "StorageClass": "STANDARD"
        }
    ]
}
```

4要素ある配列が帰ってきました！
バージョン情報は保持していないようです

# 特定のサイズのファイルだけを取得する
0kbのファイルを除外するケースを考えてみます。

### コマンド
```bash
aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms \
--query 'Contents[?Size > `0`]'
```

```--query 'Contents[?Size > `0`]'```  
　オブジェクト配列の各要素のSizeの値でfilterします

### 実行結果
```bash
$ aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms --query 'Contents[?Size > `0`]'
[
    {
        "Key": "cap.jpg",
        "LastModified": "2024-05-11T03:13:47+00:00",
        "ETag": "\"5ee32753dd30e0580d8075ff5a228921\"",
        "Size": 44575,
        "StorageClass": "STANDARD"
    },
    {
        "Key": "memo.txt",
        "LastModified": "2024-05-11T03:13:14+00:00",
        "ETag": "\"6436a6c961b90c90682e8fa52e03a6f2\"",
        "Size": 1161,
        "StorageClass": "STANDARD"
    },
    {
        "Key": "shot.jpg",
        "LastModified": "2024-05-11T03:32:50+00:00",
        "ETag": "\"cd4854823f017f45f391161fb53e6c28\"",
        "Size": 209678,
        "StorageClass": "STANDARD"
    }
]
```

配列の要素数が3つになって帰ってきました！
0byteのtest.csvをfilterの結果はじけてますね

# 特定の拡張子のファイルだけを取得する
aws cliのqueryでは正規表現は使えなさそうでした
jqでfilterします

### コマンド
```bash
aws s3api list-objects-v2 \
--bucket test-batch-ope-ito-log-a8f2ms | \
jq -r '.Contents[] | select(.Key | endswith(".jpg"))'
```

```jq -r '.Contents[] | select(.Key | endswith(".jpg"))'```
　Contents[]内の各要素に対し、Keyが".jpg"で終わるものをfilterします

### 実行結果
```bash
$ aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms | jq -r '.Contents[] | select(.Key | endswith(".jpg"))'
{
  "Key": "cap.jpg",
  "LastModified": "2024-05-11T03:13:47+00:00",
  "ETag": "\"5ee32753dd30e0580d8075ff5a228921\"",
  "Size": 44575,
  "StorageClass": "STANDARD"
}
{
  "Key": "shot.jpg",
  "LastModified": "2024-05-11T03:32:50+00:00",
  "ETag": "\"cd4854823f017f45f391161fb53e6c28\"",
  "Size": 209678,
  "StorageClass": "STANDARD"
}
```

jpgである2ファイルのみ取得できました！


# 取得期間を指定する
特定の期間内にputされたオブジェクトのみを取得するケースを考えます

### コマンド
```bash
aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms \
--query 'Contents[?LastModified >=`2024-05-11T03:14`] | [?LastModified <= `2024-05-11T03:15`]'
```

```--query 'Contents[?LastModified >=`2024-05-11T03:14`]```
LastModifiedが特定の時間以降のオブジェクトをfilterしています

```| [?LastModified <= `2024-05-11T03:15`]'```
上記filter結果を、パイプ処理で再度filterしています

### 実行結果
```bash
$ aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms --query 'Contents[?LastModified >=`2024-05-11T03:14`] | [?LastModified <= `2024-05-11T03:15`]'
[
    {
        "Key": "test.csv",
        "LastModified": "2024-05-11T03:14:28+00:00",
        "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
        "Size": 0,
        "StorageClass": "STANDARD"
    }
]
```

特定の期間にPUTされたオブジェクトのみ取得できました！

# ページネーションを行う
オブジェクトの量が多い場合、ページネーション処理が必要になります

### コマンド
```bash
aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms \
--max-items 2
```

```--max-items 2```
　一度に取得するオブジェクト数の上限を意図的に下げ、ページネーションを確認します

### 実行結果
```bash
$ aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms --max-items 2
{
    "Contents": [
        {
            "Key": "cap.jpg",
            "LastModified": "2024-05-11T03:13:47+00:00",
            "ETag": "\"5ee32753dd30e0580d8075ff5a228921\"",
            "Size": 44575,
            "StorageClass": "STANDARD"
        },
        {
            "Key": "memo.txt",
            "LastModified": "2024-05-11T03:13:14+00:00",
            "ETag": "\"6436a6c961b90c90682e8fa52e03a6f2\"",
            "Size": 1161,
            "StorageClass": "STANDARD"
        }
    ],
    "NextToken": "eyJDb250aW51YXRpb25Ub2tlbiI6IG51bGwsICJib3RvX3RydW5jYXRlX2Ftb3VudCI6IDJ9"
}
```

2つのオブジェクトのみが返却されました。
また、ページネーション用のNextTokenが合わせて返却されています

### コマンド
```bash
aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms \
--max-items 2 \
--starting-token eyJDb250aW51YXRpb25Ub2tlbiI6IG51bGwsICJib3RvX3RydW5jYXRlX2Ftb3VudCI6IDJ9
```

```--starting-token eyJDb250aW51YXRpb25Ub2tlbiI6IG51bGwsICJib3RvX3RydW5jYXRlX2Ftb3VudCI6IDJ9```
　先ほど取得したTokenをリクエストに付与し、ページ処理を行います

```bash
$ aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms --max-items 2 --starting-token eyJDb250aW51YXRpb25Ub2tlbiI6IG51bGwsICJib3RvX3RydW5jYXRlX2Ftb3VudCI6IDJ9
{
    "Contents": [
        {
            "Key": "shot.jpg",
            "LastModified": "2024-05-11T03:32:50+00:00",
            "ETag": "\"cd4854823f017f45f391161fb53e6c28\"",
            "Size": 209678,
            "StorageClass": "STANDARD"
        },
        {
            "Key": "test.csv",
            "LastModified": "2024-05-11T03:14:28+00:00",
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
            "Size": 0,
            "StorageClass": "STANDARD"
        }
    ]
}
```

無事次の2オブジェクトが取得できました！

# csv形式でoutputする
S3バッチオペレーションをしたかったことを思い出しました
S3バッチオペレーションのinputとなるcsvは以下の通りです

> Examplebucket,objectkey1
Examplebucket,objectkey2
Examplebucket,objectkey3
Examplebucket,photos/jpgs/objectkey4
Examplebucket,photos/jpgs/newjersey/objectkey5
Examplebucket,object%20key%20with%20spaces

### コマンド
```bash
aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms \
--query 'Contents[?Size > `0`].[`replace_target`,Key]' \
--output text | sed 's/\t/,/g'
```

```--query 'Contents[?Size > `0`].[`replace_target`,Key]'```
　Sizeが0以上のオブジェクトに対し、"replace_target"という固定文字列と、Keyを取得しています
　ドキュメントにあるように、CSVの1カラム目はバケット名になるようなので、ファイル出力後にまとめて置換するために適当な文字列を1列目に指定しています

```--output text | sed 's/\t/,/g'```
　平文形式で出力します。TSV形式で出力されるので、sedでCSVに変換します


### 実行結果
```bash
$ aws s3api list-objects-v2 --bucket test-batch-ope-ito-log-a8f2ms --query 'Contents[?Size > `0`].[`replace_target`,Key]' --output text | sed 's/\t/,/g'
replace_target,cap.jpg
replace_target,memo.txt
replace_target,shot.jpg
```

いいかんじ！

# バージョンもあわせて出力する
バージョニングを有効化していたのですが、```list-objects-v2``` コマンドではバージョンが確認できませんでした
調べたところ、```list-object-versions``` というコマンドがあったので、そちらを使用してみました

### コマンド
```bash
aws s3api list-object-versions \
--bucket test-batch-ope-ito-log-a8f2ms 
```

### 実行結果
```bash
$ aws s3api list-object-versions --bucket test-batch-ope-ito-log-a8f2ms 
{
    "Versions": [
        {
            "ETag": "\"5ee32753dd30e0580d8075ff5a228921\"",
            "Size": 44575,
            "StorageClass": "STANDARD",
            "Key": "cap.jpg",
            "VersionId": "M0.fTixy2AUAXCD.idyCo_pdzlhiEwkC",
            "IsLatest": true,
            "LastModified": "2024-05-11T03:49:27+00:00",
            "Owner": {
                "DisplayName": "aaaaaaaaaaaaaaaaaaaa",
                "ID": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
            }
        },
        {
            "ETag": "\"6436a6c961b90c90682e8fa52e03a6f2\"",
            "Size": 1161,
            "StorageClass": "STANDARD",
            "Key": "memo.txt",
            "VersionId": "h5Mbjk9gs81zvkO2rXfe24MLctULprNp",
            "IsLatest": true,
            "LastModified": "2024-05-11T03:52:08+00:00",
            "Owner": {
                "DisplayName": "aaaaaaaaaaaaaaaaaaaa",
                "ID": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
            }
        },
        {
            "ETag": "\"cd4854823f017f45f391161fb53e6c28\"",
            "Size": 209678,
            "StorageClass": "STANDARD",
            "Key": "shot.jpg",
            "VersionId": "EQWuUogRNFOPt_npUfdTv9xFE1I2B146",
            "IsLatest": true,
            "LastModified": "2024-05-11T03:56:42+00:00",
            "Owner": {
                "DisplayName": "aaaaaaaaaaaaaaaaaaaa",
                "ID": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
            }
        },
        {
            "ETag": "\"cd4854823f017f45f391161fb53e6c28\"",
            "Size": 209678,
            "StorageClass": "STANDARD",
            "Key": "shot.jpg",
            "VersionId": "wzLukGRVNHbR4Bgh.wRoIYwEZ58big99",
            "IsLatest": false,
            "LastModified": "2024-05-11T03:50:27+00:00",
            "Owner": {
                "DisplayName": "aaaaaaaaaaaaaaaaaaaa",
                "ID": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
            }
        },
        {
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
            "Size": 0,
            "StorageClass": "STANDARD",
            "Key": "test.csv",
            "VersionId": "XbA2AR542oaNIZRxkW6LADyBZMm34r53",
            "IsLatest": true,
            "LastModified": "2024-05-11T03:49:27+00:00",
            "Owner": {
                "DisplayName": "aaaaaaaaaaaaaaaaaaaa",
                "ID": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
            }
        }
    ]
}
```

2世代あるshot.jpgがどちらのバージョンも表示されています、良いですね！

# おわりに
jqや ```--query``` コマンドのいい練習になりました！
他のAWS CLIコマンドでもガンガン使っていきたいです