シェルスクリプト(Bash)で作るTwitterクライアント

  • 30
    いいね
  • 8
    コメント

Shell Script Advent Calendarをご覧の皆様、初めまして。Piroといいます。普段はJavaScriptやRubyでのソフトウェア開発やFirefoxの法人向け技術サポートなどをしつつ、日経Linux誌シス管系女子という連載マンガを描かせて頂いております。

Linux Advent Calendarの方にGUIアプリのスクショを定期的にSlackに流すシェルスクリプトの話でエントリーしたのですが、Shell Script Advent Calendar的にはそれをこっちを投稿した方が良かったかもと今更思いつつ、今日は別の話題です。

シェルスクリプト製Twitterクライアントには恐怖!小鳥男tweet.js(同名の別実装)などいくつか実装例がありますが、自分も2015年末頃からtweet.shという汎用のTwitterクライアントを開発しています。この記事ではtweet.shをネタに、以下の事を解説します。

  • Bashスクリプト製Twitterクライアント tweet.shの簡単な使い方の解説
  • tweet.shの実装の解説
    • 文字列をBashとnkfといくつかの一般的なコマンドでURLエンコードする
    • たくさんあるパラメータを見やすい形で定義する
    • 改行区切りのパラメータのリストをキー1=値1,キー2=値2,...の形に変換する
    • OAuth 1.0認証によるWeb APIアクセスの、ほぼ0からの実装手順

開発の動機

そもそも先行実装があるのになぜ新たに自分でBashでTwitterクライアントを書くのか?という事なんですが、端的に言うと技術力のデモです。

というのも、自分が執筆しているシス管系女子はLinuxのコマンド操作やシェルスクリプトの事をマンガ形式で解説しているのですが、「ロクに技術的な知識も無い絵描きが適当なマンガ描きちらかしてんだろwwwww」と思われてはシャクだったので、「Twitterクライアントをスクラッチで書けるくらいの技術力はあるぞゴラァ!!」とアピールしたかったのです1

とはいえただ単に作ってみただけでなく、Twitterの宣伝用アカウントの自動投稿やリモート制御にこの1年間使ってきているので、実用性もそれなりにあるのではないかと思っています。
mintbot.png

なお、実際の運用はこのTwitterクライアントに皮をかぶせた形のtweetbot.shというbotスクリプトを使っています。こちらについてご興味のある方は、チャットボットAdvent Calendarの方に書いた解説を併せてご覧下さい。

つかいかた

インストール

tweet.shは以下のコマンドに依存しています。

  • curl
  • jq
  • nkf
  • openssl

まずaptyumなどでパッケージをインストールし、これらのコマンドを使える状態にしておいてください。

次に、tweet.shのリポジトリをcloneするか、メインの実装である同名のシェルスクリプトをダウンロードします。このクライアントは単一のスクリプトで完結していますので、実装であるtweet.sh(ファイル)に実行権限を与えて適当な所(~/bin/など)に設置してください。

スクリプトの設置と並行して、認証用にapps.twitter.comでアプリケーションを作成します。Webサービスではないので、URLやコールバックは特に指定しなくても大丈夫です。アプリケーションには投稿のための書き込み権限を与えておいてください。また、DMを扱いたい場合はDMの読み書きの権限も必要です。

最後に、認証情報を定義します。以下の内容で~/.tweet.client.keyの位置にファイルを作成し、作成したアプリケーションのコンシューマキーとシークレット、アクセストークンとシークレットを記入して下さい。

MY_SCREEN_NAME=投稿に使うアカウントのスクリーンネーム
MY_LANGUAGE=投稿に使うアカウントの言語
CONSUMER_KEY=xxxxxxxxxxxxxxxxxxx
CONSUMER_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ACCESS_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ACCESS_TOKEN_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

例えば、スクリーンネームがexampleで言語が日本語なら以下のようになります。

MY_SCREEN_NAME=example
MY_LANGUAGE=ja
...

スクリーンネームと言語は、ストリーミングの受信やツイートの検索、取得したツイートの種類の判別のために使われます。単にRESTで投稿するだけの場合には、これらの情報が無くても一応動きます(多分)。

使えるコマンドの一覧

これで準備完了です。後は以下のようにすればツイートできます。

$ tweet.sh post こんにちは! █

スクリプト名の直後にサブコマンドを指定し、その後に引数を指定するスタイルです。本稿執筆時点2での代表的なコマンドは以下の物があります。

  • 読み込み系コマンド
    • fetch (get, show): ツイートのURLまたはIDを指定してJSONを取得
    • search: クエリを指定してツイートを検索
    • watch-mentions (watch): メンションその他をストリーミングで受信
    • type: fetchsearchwatchで取得した個々のJSONを元に、それがRTなのか引用RTなのかといった識別を行う
  • 書き込み系コマンド
    • post (tweet, tw): ツイートを投稿する
    • reply: リプライする
    • upload: ファイルをアップロードする(postまたはreplyとの併用前提)
    • delete (del, remove, rm): ツイートを削除する
    • favorite (fav) / unfavorite (unfav): ツイートをファボる/ファボを取り消す
    • retweet (rt) / unretweet (unrt): ツイートをRTする/RTを取り消す
    • follow / unfollow: ユーザをフォローする/フォローを解除する
  • DM操作系コマンド
    • fetch-direct-messages (fetch-dm, get-direct-messages, get-dm): 最近のDMを取得する
    • direct-message (dm): DMを送る

一般用途向けのTwitterクライアントと言うよりは、他のコマンドやスクリプトと組み合わせてライブラリ的に使う事を想定しているため、各サブコマンドの戻り値はTwitterのWeb APIが返すJSON文字列そのものとなっています。どういうJSON文字列が帰ってくるのかといった情報や、各サブコマンドの詳しい使い方については、READMEをご覧下さい。

ストリーミングの受信

コマンドの中でストリーミング形式に対応しているsearchwatch-mentionsについて、補足説明です。

ハンドラを使わないストリーミング受信

searchtweet.sh -q "検索クエリ" -wという風に-wオプションを付けるとストリーミングモードになります。watch-mentionsはストリーミングのみの対応なので、特に何も指定しなくてもストリーミングモードで動作します。

ストリーミング受信時には、これらのコマンドは新しいイベントやツイートが流れてくる度にそのJSON文字列を1件=1行で標準出力に出力します。以下のようにwhile readを使えばそれぞれの結果を取り出して処理できます。

$ ./tweet.sh search -q "シェルスクリプト" -w |
    while read -r tweet; do echo "検索条件にマッチ: $tweet"; done
$ ./tweet.sh watch-mentions |
    while read -r event; do echo "イベント受信: $event"; done

このとき、read-rオプションを指定していることに注意して下さい。-rは標準入力の中のバックスラッシュをエスケープ文字ではなくバックスラッシュという文字としてそのまま受け取るオプションです。これを指定しておかないと、ツイートの中にある改行の\nまでもがイベントの区切りの改行として解釈されてしまい、1行1イベント(ツイート)という構造が崩れてしまいます。

ハンドラを使ったストリーミング受信

search-hオプション、watch-mentions-r, -t, -q, -f, -d, -sの各オプションにハンドラとなるコマンド列を指定できます。この場合も、これらのコマンドはストリーミングモードとして動作します。

この状態では、新しいイベントやツイートが流れてくる度にハンドラのコマンド列が実行されます。この時、流れてきたイベントやツイートのJSON文字列は標準入力経由で受け取る事ができます。複雑なことをしたければ、コマンド列を直接書かずにシェルスクリプトを指定するのがオススメです。

$ ./tweet.sh search -q "シェルスクリプト" -h 'echo "イベント受信: $event"'
$ ./tweet.sh watch-mentions \
    -k "キーワード1,キーワード2,..." \
    -r "echo 'リプライまたはメンション'; cat" \
    -t "echo 'RT'; cat" \
    -q "echo '引用RT'; cat" \
    -f "echo 'フォローされた'; cat" \
    -d "echo 'DMを受信した'; cat" \
    -s "echo 'それ以外にマッチしたツイート'; cat"

watch-mentionsにハンドラを指定した場合には、tweet.sh自身がイベントやツイートの種類を自動的に判別した上で適切なハンドラが実行されます。ハンドラが指定されなかった種類のイベントやツイートは、通常通り標準出力に1行ずつ出力されます。

tweet.shの実装の解説

ポイント

Twitterクライアントを作るにあたってのポイントは、以下の点でした。

  1. APIをどう叩く?
  2. APIの返す情報をどう取り扱う?
  3. 認証は?
  4. ストリーミングの受信は?

TwitterはREST形式のWeb APIを提供しているので、curlでリクエストを送ればAPIを叩けます。また、Web APIのレスポンスはJSONなので、jqを使えば簡単に情報を取り出せます。

となると、残るは認証とストリーミングの受信です。

でもその前にちょっと寄り道として、いくつか文字列の変換処理の解説を先にやっておこうと思います。

文字列をBashとnkfといくつかの一般的なコマンドでURLエンコードする

Web APIへのリクエストを組み立てる時は、文字列をURLエンコード(パーセントエンコーディング)する処理がよく出てきます。

なので、tweet.sh内でもurl_encodeというシェル関数を以下のように定義しています。

url_encode() {
  while read -r line
  do
    echo "$line" |
      nkf -W8MQ |
      sed 's/=$//' |
      tr '=' '%' |
      paste -s -d '\0' - |
      sed -e 's/%7E/~/g' \
          -e 's/%5F/_/g' \
          -e 's/%2D/-/g' \
          -e 's/%2E/./g'
  done
}

sedtr等のコマンドと同じ感覚で使えるように、この関数では内部でwhile readでループを回して、複数行を取り扱えるようにしています。入力の1行の中に\nのような文字があった時に行の区切りとして解釈されてしまう事を防ぐために、readには-rオプションを指定しています。

nkf -W8MQは、UTF-8の文字列として標準入力から渡された内容をQuoted-Printable形式で出力する指定です。Quoted-Printableというのは、こんにちはという文字列が=E3=81=93=E3=82=93=E3=81=AB=E3=81=A1=E3=81=AFみたいになるエンコーディング形式3です。

Quoted-Printableエンコーディングでは出力の1行あたりの最大文字数が76文字と定められていて、それより長くなる場合は行末に=を付けて改行された結果が出力されます4。つまりこの時点で、1行の入力が複数行になり得ます

複数行になった出力は後で1行に繋げ直すのですが、その時「行末に付けられた=」が邪魔になるので、これをsed 's/=$//'で取り除きます。

で、tr '=' '%'でQuoted-Printableの文字列の=%に置換すると、%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AFという見慣れた形式の文字列になります。

その後、nkfによって複数行に分けられた出力について、paste -s -d '\0' -で元の1行に連結5してから、「Quoted-Printableではエンコード対象になるがURLエンコードでは予約語なのでエンコードしなくてよい文字」をsed -e 's/%7E/~/g' -e 's/%5F/_/g' -e 's/%2D/-/g' -e 's/%2E/./g'で元に戻しています。

実行例
$ echo "こんにちは" | url_encode
%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF
$ 

たくさんあるパラメータを見やすい形で定義する

Web APIを呼ぶ時は、複数のパラメータを指定する事が多いです。しかし、

  local params="q=$query&lang=$MY_LANGUAGE&locale=$locale..."

のように普通に横に並べて書いてしまうと、

  • どこに何が書かれているのか読みにくいし探しにくい。
  • パラメータ名(キー)と値を個別に取り扱いにくい。
  • (OAuth 1.0認証のように)パラメータを名前順でソートしないといけない場面で、データを取り扱いにくい。

といった問題に悩まされる事になります。

シェルスクリプトで利用できる文字列操作用のコマンドは大抵はデータが行区切りになっている事を期待していますので、パラメータの一覧を加工しやすくするためには、1パラメータを1行として定義しておいた方が容易です。これは、例えば文字列の定義時に改行を\でエスケープすることでも実現できます。

  local params="\
q $query\
lang $MY_LANGUAGE\
locale $locale\
result_type recent\
count $count\
$since_id\
"

この方法の欠点は、各行の末尾に\という見た目上余計な文字が必要だという点です。また、エスケープし忘れるとエラーになってしまいます。なので、tweet.sh内の各所では以下のようにしてパラメータを行ごとに定義しています。

  local params="$(cat << FIN
q $query
lang $MY_LANGUAGE
locale $locale
result_type recent
count $count
$since_id
FIN
  )"

cat << XXXはヒアドキュメントの書式で、そこからXXXと書かれた行までをファイルの内容としてcatに渡し、出力結果(=入力そのまま)を文字列置換で変数の値として格納しています。前後に余計なコードが増えるのは残念ですが、各行はクリーンな形に保たれるため、編集時の取り扱いが容易になります。

改行区切りのパラメータのリストをキー1=エンコード済みの値1,キー2=エンコード済みの値2,...の形に変換する

上記のように、途中段階では改行区切りの形式でパラメータを定義しておくことで文字列処理での取り扱いが容易になりますが、最終的にcurlでWeb APIに送信する時にはq=%E3%82%AF%E3%82%A8%E3%83%AA&lang=ja&locale=ja...のような形に変換する必要があります。

このために、tweet.shではto_encoded_listという関数を以下のように定義しています。

to_encoded_list() {
  local delimiter="$1"
  [ "$delimiter" = '' ] && delimiter='\&'
  local transformed="$( \
    sort -k 1 -t ' ' |
    grep -v '^\s*$' |
    url_encode |
    sed 's/%20/=/' |
    paste -s -d "$delimiter" - |
    tr -d '\n')"
  echo "$transformed"
  log "to_encoded_list: $transformed"
}

この関数の標準入力には

キー1 値1
キー2 値2
...

という改行区切りの文字列が渡ってくることを期待していて、これをまずキー名でソートし(sort -k 1 -t ' ')、空行を除去し(grep -v '^\s*$')、行をまるごとURLエンコードして(url_encode)、その結果の最初の%20=に戻して(sed 's/%20/=/')、pasteで任意の区切り文字(省略時は&)で1行に連結しています。これで、上記のような入力からキー1=エンコード済みの値1&キー2=エンコード済みの値2&...という出力を得られます。また、このとき最後に改行文字が付与されるので、tr -d '\n'で取り除いています。

区切り文字を変えられるようにしているのは、Web APIのURLパラメータ等で渡す時と、後述するOAuth 1.0の認証情報のヘッダに含める情報とで各パラメータの区切り文字が変わるからです。

実行例
$ cat params.txt 
q 検索クエリ
result_type recent
lang ja
$ cat params.txt | to_encoded_list
lang=ja&q=%E6%A4%9C%E7%B4%A2%E3%82%AF%E3%82%A8%E3%83%AA&result_type=recent
$ cat params.txt | to_encoded_list ','
lang=ja,q=%E6%A4%9C%E7%B4%A2%E3%82%AF%E3%82%A8%E3%83%AA,result_type=recent
$ 

Twitter APIのOAuth 1.0認証

前置きが長くなりました。ここからやっとOAuth 1.0の話に入っていきます。

TwitterのWeb APIは、ストリーミングAPIも含めてすべてOAuth 1.0での認証を必要とします。

実は、Twitter公式のtwurlというコマンドを使うと、OAuth 1.0認証の実装をまるっと省略できてしまいます。が、USP友の会のTwitter bot用スクリプトの実装を見ると案外簡単そうだったので
6、せっかくだからこれも自前でやってみることにしました7

OAuth 1.0での認証情報をHTTPリクエストに付与するには、以下の2段階を経る必要があります。

署名を作る

ここでいう「署名」というのは平たく言うと、APIに送る情報(APIのURL、投稿するツイートの内容や、リプライ先のIDといったパラメータなど)を1つに繋げて秘密鍵で暗号化したものです。OAuth1.0の署名(Signature)を作成する方法という記事で詳細が分かりやすく解説されているので、是非ご一読ください。以下は、その記事で解説されている内容をtweet.shでどのように実装しているかの説明です。

手順1: キーの作成

これはtweet.sh内で定義しているシェル関数generate_signatureの中の以下の箇所が該当します。

  local signature_key="${CONSUMER_SECRET}&${ACCESS_TOKEN_SECRET}"
手順2: データの作成

これはシェル関数generate_signatureの以下の箇所が該当します。

  local encoded_url="$(echo "$url" | url_encode)"
  local signature_source="${method}&${encoded_url}&$( \
    to_encoded_list |
    url_encode |
    # Remove last extra line-break
    tr -d '\n')"

先の記事の「パラメータの組み立て」と「パラメータをエンコード」は、この文字列置換の中にあるto_encoded_listurl_encodeでやっています。to_encoded_listは何も入力を取っていないように見えますが、内部でreadを実行しているので、結果的にはgenerate_signatureに渡された標準入力をそのまま受け取っている形となります。また、最後には末尾に付け加えられる余計な改行文字をtr -d '\n'で取り除いています。

その結果を文字列置換として埋め込む事で、これ全体で「パラメータの組み立て」「パラメータをエンコード」「3つの要素を&で繋ぐ」の3ステップを行っていることになります。

なお、一旦この処理の結果をtransformedというローカル変数に格納しているのは、デバッグ情報を出力しやすくするためですので、本質的な処理とは無関係です。

手順3: キーとデータを署名に変換

最後に、データとキーを使って署名を作ります。これはgenerate_signatureの以下の箇所が該当します。

  local signature=$(echo -n "$signature_source" |
    openssl sha1 -hmac $signature_key -binary |
    openssl base64 |
    tr -d '\n')

opensslコマンドでHMAC-SHA1のハッシュ値を得て、それをBase64エンコードしています。また、ここでも改行文字が付与されるので、tr -d '\n'で取り除いています。

署名を使って認証情報のヘッダを作る

署名を作るだけではOAuth 1.0認証はできません。これをHTTPのAuthorizationヘッダで送る必要があります。

Authorizationヘッダの内容は、シェル関数call_apiの以下の箇所で求めています。

  local params=''
  if [ -p /dev/stdin ]
  then
    params="$(cat)"
  fi

  local oauth="$(echo "$params" | generate_oauth_header "$method" "$url")"
  local headers="Authorization: OAuth $oauth"

標準入力で渡ってきたパラメータ(先の改行区切りのデータです)を後で複数回使うため、一旦paramsという変数に格納しています。それをgenerate_oauth_headerという別のシェル関数に渡して、OAuth 1.0の認証用ヘッダ文字列を生成しています。

generate_oauth_headerは以下のように定義しています。

generate_oauth_header() {
  local method=$1
  local url=$2

  local common_params="$(common_params)"

  local signature=$(cat - <(echo "$common_params") | generate_signature "$method" "$url")
  local header=$(cat <(echo "$common_params") <(echo "oauth_signature $signature") |
    to_encoded_list ',' |
    tr -d '\n')

  echo -n "$header"
  log "HEADER: $header"
}

common_paramsは、OAuth認証のヘッダを作る時に複数回登場するoauth_consumer_keyなどのパラメータを改行区切りで出力するシェル関数で、以下のように定義しています。

common_params() {
  cat << FIN
oauth_consumer_key $CONSUMER_KEY
oauth_nonce $(date +%s%N)
oauth_signature_method HMAC-SHA1
oauth_timestamp $(date +%s)
oauth_token $ACCESS_TOKEN
oauth_version 1.0
FIN
}

oauth_nonceはリクエストごとにユニークな文字列ということなので、ここでは現在時刻をナノ秒単位で取得して使用しています。この関数の出力を

  local common_params="$(common_params)"

で同名の変数に代入して「インスタンス化」しています。というのも、上記のシェル関数をそのまま何度も呼ぶとoauth_nonceoauth_timestampの値が変化してしまうので、同じ値を何度も使い回せるように一旦静的な文字列にしているというわけです。

その上で、それを使って

  local signature=$(cat - <(echo "$common_params") | generate_signature "$method" "$url")

ここで「この関数自体の標準入力(-)=ツイート内容などのパラメータ」と「echo "$common_params"の出力結果(<(echo "$common_params")8)=OAuthヘッダ生成用の共通パラメータ」をcatに渡して連結したものを前述のgenerate_signatureに渡し、「署名」を作っています。そしてそれを

  local header=$(cat <(echo "$common_params") <(echo "oauth_signature $signature") |
    to_encoded_list ',' |
    tr -d '\n')

  echo -n "$header"

ここでoauth_signatureという名前の新たなパラメータとして共通パラメータに連結し、改行区切りのリストからキー1=値1,キー2=値2,...というカンマ区切りのURLエンコード済みリストに変換しています。なお、ここでも一旦その結果をheaderというローカル変数に格納していますが、これもデバッグ用で、本質的な処理とは無関係です。

……という処理の結果を使って、

  local oauth="$(echo "$params" | generate_oauth_header "$method" "$url")"
  local headers="Authorization: OAuth $oauth"

ここでAuthorization: OAuth oauth_consumer_key=xxx,oauth_nonce=xxx,oauth_signature=xxx,oauth_signature_method=HMAC-SHA1,oauth_timestamp=xxx,oauth_token=xxx,oauth_version=1.0というヘッダを作っているというわけなのでした。

OAuth 1.0の認証ヘッダを付けてリクエストを送る

後はもう、ここまでで作ったヘッダを付けてリクエストを送るだけです。

curl --header "$headers" \
     --data "$(echo "$params" | to_encoded_list)" \
     $url

(実際には他にもオプションを指定していますが、ここでは省略しています。)

という感じでBashベースでOAuth 1.0認証してみたわけですが、最初に書いた通りtwurlを使えばこのあたりのことはすっかりtwurlに任せてしまえます。よっぽど酔狂な人以外は得をしない解説にお付き合い頂いた皆さん、お疲れさまでした……

ストリーミングの受信

TwitterのWeb APIにはストリーミングAPIがいくつかあります。

例えばtweet.shのwatch-mentionsでは、User streamsを使っています。これは自分へのメンションやフォローされた・RTされたといったイベントが通知されるAPIで、trackというパラメータでキーワードを指定しておくと、そのキーワードにマッチするツイートも(フォローしていないユーザのツイートも)流れてくるというものです。

「流れてくる」というのはどういう事かというと、平たく言えば、APIが返すHTTPレスポンスのBODYの長さが不定の状態で、1行あたり1イベントとして少しずつレスポンスが返ってくるという事です。curlなら、標準出力にレスポンスが1行ずつ出力されるという結果になります。

という説明からもうお分かりかと思いますが、tweet.shでもこれをwhile readで読み取って、jqで情報を取り出して種類を判別した後適切なハンドラを実行するというような事をしています。

curl --get ... https://userstream.twitter.com/1.1/user.json | while read -r event
do
  # eventにはツイートやイベントを表すJSON文字列が格納されている。
  # 必要な情報はjqを使って取り出す。
done

実際には、

  call_api GET https://userstream.twitter.com/1.1/user.json |
    handle_mentions "$user_screen_name" "$@"

といった具合にcurlの結果をパイプラインでシェル関数に渡して、受け取った側の関数handle_mentionsの中でwhileループを回しています。

その他のこと

tweet.shは本稿執筆時点2で1400行ほどありますが、外部から受け取るのは(設定ファイルの認証情報以外は)標準入力と引数だけですし、出力も標準入力とcurlによるWeb APIへのアクセスのみで、それ以外のほとんどの箇所は文字列の加工か条件分岐です。また、処理効率よりもロジックの読み取りやすさを優先しているために、そんなに凝ったテクニックは使っていない比較的素直な(愚直な)Bashスクリプトになっています。なるべく細かい単位で関数を分けるようにしているので、その処理が一体何をする物なのかという事については比較的容易に読み取れるのではないかと思います。

答え合わせをする感じでスクリプト本体を読んでみて、「なるほど、これをやるにはこうすればいいのか」という事を読み取って頂き、ご自身でシェルスクリプトを書かれる際に知見として活かして頂ければ幸いです!
thumbsup.png

「シス管系女子」について

最後にシス管系女子の事も改めてご紹介します。

自分は2011年から日経Linux誌上で「シス管系女子」というケーススタディ形式の解説マンガ記事を連載させて頂いています。

ブログ等で紹介されているコマンド列をコピペ実行はできても、そのコマンド列をゼロから自分で組み立てられないのでは、ちょっと前提が変わっただけでお手上げになってしまいます。また、理解が怪しい状態では、Webサービスの開発や運用でサーバーにSSHでログインしてコマンドで操作しないといけないという場面でも、何が起こるかよく分からなくて物怖じしてしまうのは致し方ないでしょう(筆者もそうでした)。

なので「シス管系女子」では、「個々のパーツのはたらきや仕組みをちゃんと理解して、必要に応じて手順や要素などのパーツを入れ換えたり調整したりといった応用ができる」というレベルになる事を意識して解説をしています。上記のような不安を持っていた方から「実際に参考になった」という感想を頂くこともあり、また、上司や先輩の方が新人や後輩のための1冊としてご紹介して下さっている事例も結構あると伺っています。

もし良かったら、お近くの大きめの書店でシス管系女子の本や掲載誌を手に取ってチラ見して頂けましたら幸いです。

あと、この記事で解説したTwitterクライアントを使って運用中のみんとちゃんbotアカウントではイラスト9や本編に入りきらなかった小ネタを流したり、Webサイトの方にも連載や本では扱わなかったもっと基礎的な話の特別編を置いていたりします。「シス管系女子」をテーマにしたAdvent Calendarも公開&参加者募集中なので、そちらも見て頂けましたら幸いです。

以上、Shell Script Advent Calendarの5日目でした。6日目はYoshihiko Takeuchiさんによる記事です。お楽しみに!


  1. ……というのは半分くらい冗談です。実の所は、Web上での広報活動を自動化して楽をできないか?というのが発端で、どうせなら自前で賄えばネタになるかなと軽い考えで開発をスタートしたのでした。 

  2. 2016年12月5日現在。 

  3. 入力を8bitごとに分けて、それぞれの16進数表記の前に=を付けた物。 

  4. nkfの場合は72文字で区切られます。 

  5. pasteは複数の入力をTSV形式の各列として連結するためのコマンドですが、-sを指定すると、1列を1行に変換するという動作になります。ここでさらに区切り文字を-d '\0'と指定することで、一般的なプログラミング言語の配列操作でarray.join('')とするのと同等の結果を得ています。なお、LinuxのpasteやFreeBSDのpasteではpaste -s -d ''だけで期待通りの結果を得られるのですが、macOS(OS X)標準のBSD pasteではpaste -s -d '\0' -という具合に区切り文字に明示的に\0(null文字=文字無しを意味するコード)を指定し、入力ファイルにも明示的に-(標準入力)を指定する必要がありました。macOSでも動作する書き方はLinuxでもFreeBSDでも元と同じ結果を得られたので、ここではその書き方に揃えています。 

  6. 単に、その時twurlの存在を知らなかったからというのもあります。というか最近になってやっと存在を知りました。 

  7. ということで、tweet.shにおけるOAuth 1.0での認証の実装はUSP友の会のTwitter bot用スクリプトの実装を色々と改変する形で作られています。tweet.shのソースのライセンス表記にTomoToolの名があるのはこういう経緯のためです。スクラッチしたと言っておきながら肝心な所は自作ではなかったのでした。 

  8. この書き方は「プロセス置換(Process Substitution)」というBashの機能です。 

  9. これまでのイラスト一覧