GAS x SHELLでGoogleスプレッドシートを効率的に？ダウンロード

今回はGASとシェルを用いて，Google SpreadsheetをCSV形式で一括ダウンロードする方法をご紹介します．
# 概要
* スプレッドシートを一時的に「リンクを知っている人全員」にできること
* Unix・Linux系のシェルコマンドが実行できること
* wgetが使える
* “<シート名>.csv”というファイル名で保存される

# ソースコード
先に今回利用するコードを示します．これをダウンロードしたいスプレッドシートに紐づいたGASプロジェクトに貼り付けてください．
```javascript
function generateCsvDownloadShellScript() {
  const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
  const sheets      = spreadSheet.getSheets();
  const url         = spreadSheet.getUrl().replace("edit", "export?gid=");

  const gids       = sheets.map(sheet => sheet.getSheetId());
  const sheetNames = sheets.map(sheet => sheet.getSheetName());

  const gidCommand       = `gids=(${gids.join(" ")});`;
  const sheetNameCommand = `sheetNames=("${sheetNames.join('" "')}");`;
  const downloadCommand  = `for i in {1..${gids.length}}; do wget -O "\${sheetNames[\$i]}.csv" "${url}\${gids[\$i]}&format=csv"; done;`;

  const command = `${gidCommand} ${sheetNameCommand} ${downloadCommand}`;
  
  console.log(command);
  return;
}
```
# 実行手順
CSVをダウンロードする手順は以下です
1. 上記のコードをダウンロードしたいスプレッドシートに紐づいたGASプロジェクトで実行する
1. 実行後に，console.logによって表示されたコマンドをコピー
1. スプレッドシートの共有範囲を「リンクを知っている全員」に変更
1. ターミナルで任意のディレクトリに移動
1. コピーしたコマンドを実行
1. 実行したディレクトリに<シート名>.csvが保存される
1. 必要なら，スプレッドシートの共有範囲を戻す

# 説明
## wgetのやってること
```shell
wget -O　FILENAME.csv 'https://docs.google.com/spreadsheets/d/<ここは乱数>/export?gid=<ここにダウンロードしたいシートのgid>&format=csv'
```
上記のコマンドによって，スプレッドシートの任意のシート(シート1やシート2)をcsvとしてダウンロードできます．gidはシート1やシート2によって異なるので適宜変更してください．

## ソースコード
```javascript
const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
const sheets      = spreadSheet.getSheets();
const url         = spreadSheet.getUrl().replace("edit", "export?gid=");
```

上記のコードでは，まず，sheetsという配列に各シートオブジェクトが要素となった配列を定義しています．

また，スプレッドシートのURLを取得しています．getUrlメソッドの返り値は”https://docs.google.com/spreadsheets/d/<ここはランダム>/edit”みたいな感じの文字列が返ってきます．wgetによって取得したいので，末尾を”edit”から”export?gid=”に置換しています

次に，

```javascript
const gids = sheets.map(sheet => sheet.getSheetId());
/* example: gid = ["747889", "899997", "834701", "0473103"] */

const sheetNames = sheets.map(sheet => sheet.getSheetName());
/* esample: shhetNames = ["シート1", "シート2", "シート3", "シート4"] */
```

上記のコードでは，スプレッドシートに含まれる全シートの，gidとシート名を配列として取得しています．

最後に，

```javascript
const gidCommand       = `gids=(${gids.join(" ")});`;
const sheetNameCommand = `sheetNames=("${sheetNames.join('" "')}");`;
const downloadCommand  = `for i in {1..${gids.length}}; do wget -O "\${sheetNames[\$i]}.csv" "${url}\${gids[\$i]}&format=csv"; done;`;

const command = `${gidCommand} ${sheetNameCommand} ${downloadCommand}`;
```

上記のコードで，文字列としてのシェルコマンドを生成しています．ソースコードベースだとわかりにくいですが，単純にシェルコマンドが生成されます．生成されるシェルコマンドの例を示します．

```shell
gids=(10000 20000 30000 400000); sheetNames=("シート1" "シート2" "シート3" "シート4"); for i in {1..4}; do wget -O "${sheetNames[$i]}.csv" "https://docs.google.com/spreadsheets/d/<シートのid>/export?gid=${gids[$i]}&format=csv"; done;
```

上記が生成されるシェルコマンドの例です．gids=()とsheetNames=()はシェルにおける配列です．あとはgidとシート名をもとにfor文でwgetを繰り返します．

# メリット/デメリット
少し特殊な方法なので個人的に感じるメリット/デメリットを書いておきます．

## メリット
GASには最低限のことをさせて，重要な部分はシェルコマンドであることが最大のメリットです．

## デメリット
共有範囲の設定が脆弱なことが最大のデメリットです．今回の方法では，スプレッドシートが数分間程度ではあるものの世界に公開されます．秘匿性の高い情報を扱っている場合には利用できません．そんな秘匿性の高い情報をスプレッドシートで管理しているかは別として，この辺をセキュアに突破できるスマートな方法があればまた別の記事で書くかもしれません．

# おわりに
今回はここまでです．スプレッドシートをcsvで扱いたいけど，マウスでぽちぽちなんてやってられるかという感性のあなたに刺されば幸いです．