しるてく

技術的な話をします

制御文字の検索と置換

背景

ブログのRSSで概要しか出していなかったところを全文出そうとしたら Input is not proper UTF-8, indicate encoding ! みたいに言われた。 おそらくどこかの記事で制御文字が含まれているのでなんとか見つけ出して撲滅したい。

やりかた

検索

git grep -IP '[[:cntrl:]](?<![\n\r\t])'

置換

find . -type f -name '*' | xargs perl -i -pe 's/[[:cntrl:]](?<![\\n\\r\\t])//g'

コマンドラインから直近の会議の zoom を開く

概要

在宅勤務となり、ほぼすべての会議が zoom となったが、Google Calendar から zoom を開くまでが色々とだるいので楽したい。

これまで

  1. Google Calendar を開き
  2. 予定の詳細を表示して
  3. zoomのURLをクリックすると
  4. 新しいタブが開き
  5. アプリケーションを開く許可を求められるので
  6. 「zoom.usを開く」
  7. タブを閉じる

参考:こんなページが表示される

f:id:ofsilvers:20200421180024p:plain

これから

$ meeting
Your next meeting is "会議その1", organized by silvers.
It started 59 minutes ago.
Calendar event URL: https://www.google.com/calendar/event?eid=xxxxxxxxxx
Zoom URL: zoommtg://zoom.us/join?confno=xxxxxxxxxx
Open Zoom? (y/N)n
_____________________________________________________
Your next meeting is "会議その2", organized by silvers.
It starts 17 seconds from now.
Calendar event URL: https://www.google.com/calendar/event?eid=xxxxxxxxxx
Zoom URL: zoommtg://zoom.us/join?confno=xxxxxxxxxx
Open Zoom? (y/N)y

やりかた

1. zoom-go を入れる

github.com

流れに沿ってインストールして設定すれば zoom で次の予定を表示させることができる。 問答無用で次の予定に参加するぞ!という感じであれば

$ zoom | grep zoommtg | cut -d ' ' -f 3 | xargs open

とかで十分。

複数会議が入り乱れているときとか意図せぬ会議を開いてしまいそうな人は次へ。

2. 対話的に開けるようにする

gist.github.com

こんな感じのやつをPATHの通ったところに置く。 1個1個予定を表示しつつ、「これ開く?」って聞いてくれる。

2020/04/22 追記

5分前行動のせいで気付かなかったんだけど、最初の予定が5分以内なら自動で開始する機能があるっぽかったので、予定が連続していたり入り乱れていない場合以外は zoom だけで十分かも。

https://github.com/benbalter/zoom-go/blob/d101021033af194490395637214d049833d02b92/zoom.go#L106-L114 https://github.com/benbalter/zoom-go/blob/c89e5ec469473c42998057efe49fbc450cf84bb8/cmd/zoom/main.go#L134-L143

draw.io で Alibaba Cloud のアイコンを使いたい

draw.io はVisioとの互換性高いので、 https://www.alibabacloud.com/help/doc-detail/67830.htm で配布されている Microsoft Visio template(.vssx) がそのまま使える。 ダウンロードしてドラッグすれば良い。

f:id:ofsilvers:20190903183457p:plain

ScratchPadはブラウザのローカルストレージを使っているので永続化したければ、鉛筆のアイコンから Export してライブラリにしておくと良い(次回からは File > Open Library from から開ける)*1

Alibaba Cloud はじめるまで

次のISUCONでAlibaba Cloudを使うのでちょっと触ってみた。

www.alibabacloud.com

プロダクトとサービス

たくさんあるけど今回はECS(Elastic Compute Service)を使う。

f:id:ofsilvers:20190902100716p:plain

aliyun

管理画面でぽちぽちもできるけど、面倒なのでCLIを使う。 なんでaliyunなのかと思ったら、インターナショナルでは "Alibaba Cloud" という名前だけど、中国では "Aliyun / 阿里雲" というサービス名だかららしい。

github.com

RAM(Resource Access Management) > ユーザ > 管理 > AccessKey からAccessKeyを作成して aliyun configure で設定する。

$ aliyun configure
Configuring profile 'default' ...
Aliyun Access Key ID [None]: <Your AccessKey ID>
Aliyun Access Key Secret [None]: <Your AccessKey Secret>
Default Region Id [None]: ap-northeast-1
Default output format [json]: json
Default Languate [zh|en]: en

Configure Done!!!
..............888888888888888888888 ........=8888888888888888888D=..............
...........88888888888888888888888 ..........D8888888888888888888888I...........
.........,8888888888888ZI: ...........................=Z88D8888888888D..........
.........+88888888 ..........................................88888888D..........
.........+88888888 .......Welcome to use Alibaba Cloud.......O8888888D..........
.........+88888888 ............. ************* ..............O8888888D..........
.........+88888888 .... Command Line Interface(Reloaded) ....O8888888D..........
.........+88888888...........................................88888888D..........
..........D888888888888DO+. ..........................?ND888888888888D..........
...........O8888888888888888888888...........D8888888888888888888888=...........
............ .:D8888888888888888888.........78888888888888888888O ..............

aliyun の使い方は --help とか見ていけばだいたい使い方は分かる。 Linkでヘルプページが記載されているんだけど中国語なので、英語版のドキュメントは https://www.alibabacloud.com/help を見る。

$ aliyun ecs --help
Alibaba Cloud Command Line Interface Version 3.0.24

Usage:
  aliyun Ecs <ApiName> --parameter1 value1 --parameter2 value2 ...

Product: Ecs (Elastic Compute Service)
Version: 2014-05-26
Link: https://help.aliyun.com/api/ecs

Available Api List:
...
...

DescribeInstancesDescribeSnapshots とかで情報が見れるが結構帰ってくる json がでかいので jq とかを使うのが吉。

Google Form送信時に情報をslackに通知をする

そういう機能が欲しかったのでGAS書いた。

手順

1. フォームを作る

適当に好きなフォームを作る。

2. スクリプトエディタからGASを書く

f:id:ofsilvers:20170816141110p:plain:w250

画面右上のメニューから選択する。

3. スクリプト書く

function onFormSubmit(e) {
  sendToSlack(e);
}

function sendToSlack(e) {
  var token = 'FIXME'; // your token
  var channel = 'FIXME'; // e.g. '#random'
  var username = 'FIXME';
  var icon_emoji = 'FIXME'; // e.g. ':beer:'
  
  var payload = {
      'token' : token,
      'channel' : channel,
      'text' : createText(e),
      'username' : username,
      'parse' : 'full',
      'icon_emoji' : icon_emoji
  };

  var params = {
      'method' : 'post',
      'payload' : payload
  };

  var response = UrlFetchApp.fetch('https://slack.com/api/chat.postMessage', params);
}

function createText(e) {
  return '適当なメッセージ';
}

4. トリガを設定する

f:id:ofsilvers:20170816141743p:plain

スクリプトエディタの時計みたいなところから以下のような感じでトリガを設定する。

f:id:ofsilvers:20170816141827p:plain

投稿内容の取得例

Class FormResponse  |  Apps Script  |  Google Developers

投稿した人

  var mail = e.response.getRespondentEmail();
  var user = mail.split('@')[0];

もし会社とかでドメインが一緒なら@以前がおそらく社員の名前。

投稿内容

  var res = e.response.getItemResponses();
  var target = res[0].getResponse(); // 1個目の質問の回答
  var comment = res[1].getResponse(); // 2個目の質問の回答

crontabでdateを扱うときとかに注意すること

45  9 * * 3 hoge.pl `date -v+2d +"%Y%m%d"`

こんな感じのことやろうとしたら

/bin/sh: -c: line 0: unexpected EOF while looking for matching ``'
/bin/sh: -c: line 1: syntax error: unexpected end of file

こけてなんだろなーと思ったらcrontabでは % 以降を標準入力として扱うらしくエスケープが必要らしい。

みんながハマった道だと思いつつメモ残しておく。

Androidのパッケージ名に使える文字

AppStoreとあわせようとしてハマったのでメモ。

基本は英字。 先頭でなければ 数字と_が使える。

https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/content/pm/PackageParser.java#1201

    private static String validateName(String name, boolean requireSeparator,
            boolean requireFilename) {
        final int N = name.length();
        boolean hasSep = false;
        boolean front = true;
        for (int i=0; i<N; i++) {
            final char c = name.charAt(i);
            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
                front = false;
                continue;
            }
            if (!front) {
                if ((c >= '0' && c <= '9') || c == '_') {
                    continue;
                }
            }
            if (c == '.') {
                hasSep = true;
                front = true;
                continue;
            }
            return "bad character '" + c + "'";
        }
        if (requireFilename && !FileUtils.isValidExtFilename(name)) {
            return "Invalid filename";
        }
        return hasSep || !requireSeparator
                ? null : "must have at least one '.' separator";
    }