Mac iTerm2でコマンドインタラクションなどの後にスクロールバックできなくなった場合の設定の戻し方

M1 Macbook pro にリプレースしてうきうきなのですが、 iTerm2 利用中、"A control sequence attempted to clear scrollback history. Allow this in the future?" というダイアログにちょっとつまづいたのでメモです。

同じように困った人がいそうなので。

問題

f:id:kan-getsu:20211124013658p:plain
インタラクション

"Always Allow" を選択すると、インタラクションから戻った後にインタラクション以前のコンソールの表示をスクロールしてさかのぼれなくなってしまう

解決法

  • iTerm2 起動中の上のメニューから「Profiles」を選択し、「Open Profiles」>「Edit Profiles...」>「Advanced」>「Prevent CSI 3 J from clearing scrollback history?」を「Yes」にすればスクロールバックできるようになる
  • 右上の検索ボックスから「scrollback」などで検索すると楽 (検索場所を間違えると表示されないので注意)

プロセスを grep するときに grep プロセス自身を結果から除外する Tip とその理屈

プロセスの grep を業務柄よくやるのですが, grep 自身のプロセスが引っかかるのがきれいじゃなくて気になっていました*1

(↓の grep --color=auto sshd の部分)

kangetsu@ubuntu18:~
$ ps auxf | grep sshd
root       641  0.0  0.3  72308  6324 ?        Ss   01:27   0:00 /usr/sbin/sshd -D
root      4524  0.0  0.3 107996  7092 ?        Ss   03:44   0:00  \_ sshd: kangetsu [priv]
kangetsu  4557  0.0  0.2 108360  5064 ?        S    03:44   0:00  |   \_ sshd: kangetsu@pts/0
kangetsu  4952  0.0  0.0  13144  1016 pts/0    S+   04:19   0:00  |           \_ grep --color=auto sshd
root      4527  0.0  0.3 108000  7088 ?        Ss   03:44   0:00  \_ sshd: kangetsu [priv]
kangetsu  4576  0.0  0.1 108000  3624 ?        S    03:44   0:00  |   \_ sshd: kangetsu@notty
root      4635  0.0  0.3 107996  7080 ?        Ss   03:57   0:00  \_ sshd: kangetsu [priv]
kangetsu  4656  1.7  0.2 108360  5080 ?        S    03:57   0:22  |   \_ sshd: kangetsu@pts/1
root      4639  0.0  0.3 108000  7084 ?        Ss   03:57   0:00  \_ sshd: kangetsu [priv]
kangetsu  4690  0.0  0.1 108000  3512 ?        S    03:57   0:00      \_ sshd: kangetsu@notty
kangetsu@ubuntu18:~

はじめは | grep -vw grep などで除外してましたが, [] を使うというもっとスマートな方法がありました。
その解説をした記事は複数あるのですが, 読んだだけではピンとこなかったので自分の納得のために理屈をまとめたのがこの記事です。

TL;DR

  • grep の引数の文字列の先頭を [] で囲めば OK
    • [] で囲む文字は先頭でなくても OK, 複数でも OK (やる意味はない)
    • e.g., ps auxf | grep [h]oge
    • GNU grep で確認, 他は未確認

*1:最近は ps たくさん打ったので感覚が麻痺した

続きを読む

JavaScript (ES6 対応) による CSV パーサの実装

副業で Google Apps Script (GAS) を使っているのですが、CSV のパースに困っていました。
GAS には Utilities.parseCsv という CSV パーサがあるのですが, 本記事執筆時点で「同一セルに改行区切りで複数値が入っていても解釈できない」という仕様になっており、自分でパーサを実装する必要に迫られたためです。
とは言え先人が (目的は違いますが) 実装を試みてくれており, 検索すれば JS の CSV パーサの実装は見つかります。
ただし今の私ではその実装を見てすぐに理解できなかったので, 今回はそのコードを ES6 対応にし, 処理が理解しやすいよう書き換えました。

参考にさせていただいたコードは, Qiita に掲載されていた weal さんの記事 にあるものです (weal さん, ありがとうございました!)。

結局このコードは実際に使う必要はなくなったのですが, 勉強になったし折角なのでここに供養します。

続きを読む

JavaScript での意図しないグローバル変数の生成に注意

TL;DR

  • JavaScript, Google Apps Script では思わぬバグ防止のために strict mode を使う
    • 冒頭に 'use strict'; と書けばよい*1
  • 変数の巻き上げによる思わぬバグ防止のために var は使わず let, const を使う

何が起きたか

 先日 Google Apps Script を書いていました。Google Apps Script (GAS) は JavScript で記述することができ*2、2021年5月現在では V8 Runtime にも対応し、よりモダンな JavaScript の文法が利用できます*3

私は JavScript はほぼ経験がなかったのですが、別件で JavaScript Primer でその前にたまたま学習していたので、思っていたより気軽に書くことができました。

ところが、動作確認中に意図しない動作になっており、スクリプトを見直しても怪しいところはすぐには見つけられませんでした。具体的には、以下のようなスクリプトです (一部を抜粋、変数名などは適当に変えています)。

デバッグ中に foo の値が意図しないものになっていたことに気づきました。JavScript に慣れた方ならすぐ問題点がわかるかもしれません。

// ...
function getFooLink(fooName) {
  // ...
  for (foo of fooInfo) {
    if (foo[0] === fooName) {
      return foo[1];
    }
  }
}
// ...
function main() {
// ...
    let fooLinkArray = [];
    for (foo of foos) {
      let fooLink = getFooLink(foo);
      fooLinkArray.push(`${foo}\n${fooLink}`);
    }
// ...
}

原因:何が問題だったか

 問題の原因を書くと、for 文の中での一時変数の宣言時に、letconst 等を使っていないこと でした。JavaScript では、宣言子なしに変数宣言をすると、その変数がグローバル変数となります*4

私が普段一番使う言語が Python なのですが、Python では変数の宣言子はありません。普段の変数宣言では letconst を使うようにもちろん意識していたのですが, for (x of y) {} の構文では微妙に Python と似ている (と思う) せいか, 宣言子の利用を忘れてしまっていました。これが for (let i = 0; i < num; i++) {} のような昔ながらの構文で書いていたら忘れていなかったと思います。

基本的なことかもしれませんが、こんなミスをしてしまっていました。

検証

 上記のような、スコープの変化を検証してみます。

  1. for ブロック内での参照
  2. for ブロック外、同一関数内での参照
  3. グローバルスコープからの参照

の順にそれぞれ見ています。

  • 検証環境:Google Chrome 90.0.4430.212(Official Build) (64 ビット)

1. let

for (let e of array) {}

のパターンです。for のブロックを抜けると参照できなくなります

function scopeTest() {
    let array1 = ['a', 'b', 'c'];
    for (let e1 of array1) {
        console.log(e1);  // 1
    }
    console.log(e1);  // 2
}
scopeTest();
VM267:4 a
VM267:4 b
VM267:4 c
VM267:6 Uncaught ReferenceError: e1 is not defined
    at scopeTest (<anonymous>:6:17)
    at <anonymous>:1:1
console.log(e1);  // 3
VM339:1 Uncaught ReferenceError: e1 is not defined
    at <anonymous>:1:13

2. const

for (const e of array) {}

のパターンです。let と同じく、for のブロックを抜けると参照できなくなります

function scopeTest2() {
    let array2 = ['x', 'y', 'z'];
    for (const e2 of array2) {
        console.log(e2);  // 1
    }
    console.log(e2); // 2
}
scopeTest2();
VM345:4 x
VM345:4 y
VM345:4 z
VM345:6 Uncaught ReferenceError: e2 is not defined
    at scopeTest2 (<anonymous>:6:17)
    at <anonymous>:1:1
console.log(e2);  // 3
VM415:1 Uncaught ReferenceError: e2 is not defined
    at <anonymous>:1:13

3. var

 ここで、最近の JavaScript では使うことはありませんが、var で宣言した場合も見ます。

for (var e of array) {}

var を使った場合は 変数の巻き上げ*5という特殊な挙動が起き、グローバル空間からは参照できませんが、スコープの一つ外から参照できてしまいます。より正確には、変数宣言の部分が一つ外のスコープに暗黙的に移動されてしまいます (詳細は脚注のリンク先を見てみてください)。

巻き上げが起きた結果、1だけでなく 2でも参照できてしまう、ただし 3からは参照できない、という結果になっています。

この挙動は多くの初学者を惑わせており、この問題解決のために letconst が仕様となったようです。

function scopeTest3() {
    let array3 = ['g', 'h', 'i'];
    for (var e3 of array3) {
        console.log(e3);  // 1
    }
    console.log(e3);  // 2
}
scopeTest3();
VM420:4 g
VM420:4 h
VM420:4 i
VM420:6 i
console.log(e3);  // 3
VM492:1 Uncaught ReferenceError: e3 is not defined
    at <anonymous>:1:13

4. 宣言子なし

 最後に、私が今回やらかしてしまった、宣言子なしのパターンです。
1, 2, 3 いずれからも参照できており、確かにグローバル変数となっています。

function scopeTest4() {
    let array4 = ['j', 'k', 'l'];
    for (e4 of array4) {
        console.log(e4);  // 1
    }
    console.log(e4);  // 2
}
scopeTest4();
VM100:4 j
VM100:4 k
VM100:4 l
VM100:6 l
console.log(e4);  // 3
VM182:1 l

簡単な対策

 今回の私のような失敗、すなわち 宣言子を忘れて意図しないグローバル変数を生み出す ことは、実は strict mode を使えば簡単に回避できます

Google Apps Script でも strict mode がつけるようになっているので、 'use strict'; を記述しておきましょう。
JavaScript に不慣れなこと、GAS でも strict mode が使えると思っていなかったことも重なり、今回のような問題になってしまいましたが、今後は同じことを繰り返さないだろうと前向きに捉えることにします。

基本的なことですが記録。

*1:厳密には冒頭に限らない。strict mode を適用したいスコープで書けばよいが, 冒頭に書く = スクリプト全体に適用すれば普通は良い

*2:> Google Apps Script is a rapid application development platform that makes it fast and easy to create business applications that integrate with Google Workspace. You write code in modern JavaScript and have access to built-in libraries for favorite Google Workspace applications like Gmail, Calendar, Drive, and more.

*3:ただし ES6 モジュールなど、一部機能はサポートされていない

*4:https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Grammar_and_types#declaring_variables

*5:https://jsprimer.net/basic/function-scope/#hoisting-var

Amazon Kindle の電子書籍の改訂版が出た際のコンテンツ更新方法

こういうことでした。
わざわざ記事にする内容かな? と思ったけど、2021/2/11 時点でさっと検索しても (公式含めて) 古い情報だったりしたので、いつか誰かの役に立つかもしれないと思い書いておきます。

購入した Kindle 本の改訂版への更新まとめ

以下自分の解決までの道のりです。
補足情報も併せて書いています。

  • Kindle で購入したものは改訂版などが出たときに再購入しなくても更新できる (ものがある)
  • Amazon > Kindle本 > コンテンツと端末の管理 > コンテンツ から改訂版をダウンロードできる場合もあるらしい (自分はできなかったと思われるが、後述の通りすでにダウンロード済みだった可能性もある)
  • Amazon > Kindle本 > コンテンツと端末の管理 > 設定 で「本の自動更新」を「オン」にしていると自動更新されるらしい (自分はなってたけど後述の通り更新されていたか不明)
  • サポート › デジタルサービス&デバイスサポート などから「カスタマーサービスに連絡」などでチャットなどでサポートを求めると更新できる場合は更新してくれる*1
  • Kindle アプリなどから本を右クリックなどして「端末から削除」した上で、Amazon > Kindle本 > コンテンツと端末の管理 > コンテンツ から「配信」を行うと更新が反映された
    • もしかしたら初めからこれをすれば更新されたのかもしれない、デフォルトで「本の自動更新」は「オン」だと思うのでまず試してみてください

自分の無知と勘の悪さゆえにキャッシュみたいな問題に振り回されました。
なお、改訂版に更新すると ブックマーク、メモ、ハイライトなどの情報は引き継がれず消えてしまう のでそこもご注意ください。

また、くれぐれも 端末から削除するのみで、コンテンツそのものの削除はしないでください (何かあっても責任は負えません)。
現時点では、コンテンツを削除しようとすると確認を一回挟むので気づくかと思いますが、これをやると購入した書籍を再購入しないと読めなくなってしまいます。

以上、Kindle のコンテンツ更新ではまったので覚書でした。

*1:途中からボットから従業員の方に代わります、当然ですが節度ある態度と正確・詳細・簡潔な情報で質問しましょう

systemd-resolved を利用している Ubuntu では初期設定では dig +trace が機能しない件のメモ

Ubuntu 18.04 で dig+trace オプションが, server を指定しないと機能しなくてはまったので, メモを残します。

TL;DR

  • Ubuntu 16.10 くらいから systemd-resolved がスタブリゾルバとして採用されている
    • これに伴い /etc/resolv.confnameserver のデフォルト値が 127.0.0.53 になった
    • このとき, dig+trace は利用できないが dig@ で他のフルリゾルバを明示的に指定してあげると動く

何が起きたか: Ubuntu 18.04 で dig +trace が機能しない

以前の記事, dig コマンドで名前解決が再帰的に行われる様子を確認する では, dig+trace オプションを使って, ルートサーバから順に再帰的に名前解決が行われる様子を確認しました。
最近 DNS を体系的に勉強し直して, 改めて dig コマンドを +trace オプションと共に使用したところ, 以下のようなレスポンスが返ってきました。

kangetsu@ubuntu18:~
$ dig www.google.com +trace

; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> www.google.com +trace
;; global options: +cmd
;; Received 51 bytes from 127.0.0.53#53(127.0.0.53) in 0 ms

kangetsu@ubuntu18:~

以前と異なり, 結果が返ってきません。+trace を外して試してみます。

kangetsu@ubuntu18:~
$ dig www.google.com

; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> www.google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31929
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;www.google.com.                        IN      A

;; ANSWER SECTION:
www.google.com.         93      IN      A       216.58.197.164

;; Query time: 8 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Sun Jan 03 09:11:13 UTC 2021
;; MSG SIZE  rcvd: 59

kangetsu@ubuntu18:~

ちゃんと A レコードが返ってきました。

色々調べていると, 以前の記事では Ubuntu 16.04 を使っており, Ubuntu 18.04 とは /etc/resolv.conf の内容が異なっていました。

  • 16.04
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 192.168.3.1
  • 18.04
# This file is managed by man:systemd-resolved(8). Do not edit.
#
# This is a dynamic resolv.conf file for connecting local clients to the
# internal DNS stub resolver of systemd-resolved. This file lists all
# configured search domains.
#
# Run "systemd-resolve --status" to see details about the uplink DNS servers
# currently in use.
#
# Third party programs must not access this file directly, but only through the
# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,
# replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 127.0.0.53
options edns0

man 5 resolv.conf によると, nameserver はリゾルバがクエリを送るネームサーバを記述する箇所です。

[...]
       nameserver Name server IP address
              Internet address of a name server that the resolver should query, either an IPv4 address (in dot notation), or an IPv6 address in colon (and possibly dot) notation as per RFC  2373.
              Up  to MAXNS (currently 3, see <resolv.h>) name servers may be listed, one per keyword.  If there are multiple servers, the resolver library queries them in the order listed.  If no
              nameserver entries are present, the default is to use the name server on the local machine.  (The algorithm used is to try a name server, and if the query times out, try  the  next,
              until out of name servers, then repeat trying all the name servers until a maximum number of retries are made.)
[...]

ただ, DNS 周りは用語の統一があまりされておらず*1, ここで言っている nameserver がネームサーバ (権威サーバ/コンテンツサーバ) なのか, フルリゾルバ (DNS キャッシュサーバ) なのか, 非常に混乱したのですが, JPRS の 2003年の資料 など見る限りフルリゾルバで合ってそうです。
直感的にも, スタブリゾルバであるクライアントがクエリを送る先 = 名前解決要求を送る先だから, フルリゾルバなら納得感があります。

ちなみに JPRS は日本の .jp TLD を管理するレジストリです, レジストリ・レジストラについては次の記事で具体例を紹介しています。 https://www.kangetsu121.work/entry/2021/01/01/233451

脱線しましたが, フルリゾルバとして 127.0.0.53 (ローカルホスト) が指定されていることが影響していそうです。
dig では @ の後に IP アドレスなどを続けることで, 名前解決要求を送るサーバを指定できるので, ここに Ubunutu 16.04 の resolv.conf に記載されている IP アドレスを指定してみます。

kangetsu@ubuntu18:~
$ dig www.google.com A @192.168.3.1 +trace

; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> www.google.com A @192.168.3.1 +trace
;; global options: +cmd
.                       343328  IN      NS      e.root-servers.net.
.                       343328  IN      NS      d.root-servers.net.
.                       343328  IN      NS      g.root-servers.net.
.                       343328  IN      NS      j.root-servers.net.
.                       343328  IN      NS      i.root-servers.net.
.                       343328  IN      NS      k.root-servers.net.
.                       343328  IN      NS      a.root-servers.net.
.                       343328  IN      NS      m.root-servers.net.
.                       343328  IN      NS      f.root-servers.net.
.                       343328  IN      NS      c.root-servers.net.
.                       343328  IN      NS      h.root-servers.net.
.                       343328  IN      NS      l.root-servers.net.
.                       343328  IN      NS      b.root-servers.net.
.                       516960  IN      RRSIG   NS 8 0 518400 20210116050000 20210103040000 42351 . dRIVLaDX3M964hn9zstRtLGSgHeoqdSl/gygnEIdZdDeW0nykd5T+IGN bpr35G8oHBKC9nk8r4WpxxhVxAcLYNnv3zH+1a+se2/NlRSnFSdCtJjj GB0PJSijEpznqtPdBCZaoPjJ/B7VeDLn1MSrcNhiJw5ZzozFOJc6oiRJ va76X5H3lUlz6qZhLW+Zq+qt63eOe67QNwuEP0qYYxDhBcNTWyaN48rp hzGkNqAZcxtL498Dl4cFV/7OlM5kyDqVMKcMYgfRIbI8ZprO64reGFbu 9Ta6Xteb+0ojYST/SU+jeRSixdBthyi3zrJNBGkbEQlrBKQgpoup2uTh 8pcHCw==
;; Received 1097 bytes from 192.168.3.1#53(192.168.3.1) in 12 ms

com.                    172800  IN      NS      l.gtld-servers.net.
com.                    172800  IN      NS      b.gtld-servers.net.
com.                    172800  IN      NS      c.gtld-servers.net.
com.                    172800  IN      NS      d.gtld-servers.net.
com.                    172800  IN      NS      e.gtld-servers.net.
com.                    172800  IN      NS      f.gtld-servers.net.
com.                    172800  IN      NS      g.gtld-servers.net.
com.                    172800  IN      NS      a.gtld-servers.net.
com.                    172800  IN      NS      h.gtld-servers.net.
com.                    172800  IN      NS      i.gtld-servers.net.
com.                    172800  IN      NS      j.gtld-servers.net.
com.                    172800  IN      NS      k.gtld-servers.net.
com.                    172800  IN      NS      m.gtld-servers.net.
com.                    86400   IN      DS      30909 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CF C41A5766
com.                    86400   IN      RRSIG   DS 8 1 86400 20210116050000 20210103040000 42351 . HhNsG1f41O3quXsdkzVJdvxPO9/1mf/FIbv8NE68B9m/UuRhhr6aGhbn Z8IOKvd5R8EomZSgW+Bwwo9eA1+DrzESwOeANCZpQ2rMXQAw+J6tgiP2 SztHe8s8ZU5ckPE+kzgfiJ4H0HgrmN6C3xI29JTVPrX73y8sTSZeFqvb 3wvmzxmLsR0MW1H5X8BRbIROWDUvh0mD4IYDkXtyRKtKL1UDM0v50/v4 BDdzV4VlDbKg1DHlDu3tR7QUhYSQxzekgCGfCvZ84yb3FFLRoplYcCHE qrQrO7hXB1O0SoEpaxovSzb1l1Z9T80KikiFHIZGtT4NkP1k4A4W0fGg SIlggw==
;; Received 1174 bytes from 192.5.5.241#53(f.root-servers.net) in 6 ms

google.com.             172800  IN      NS      ns2.google.com.
google.com.             172800  IN      NS      ns1.google.com.
google.com.             172800  IN      NS      ns3.google.com.
google.com.             172800  IN      NS      ns4.google.com.
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q1GIN43N1ARRC9OSM6QPQR81H5M9A  NS SOA RRSIG DNSKEY NSEC3PARAM
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN RRSIG NSEC3 8 2 86400 20210109045959 20210102034959 31510 com. jaAhMvqXepGlEp5eDn/sqwz9aDzpYyTxq8/upGZoMF4xG+r+zgcP3fVz LvNHVgkd45Ow5nT+saz+Ay+n+YE64jlY41ld/JVlBQZvDV1FKjyIRjVM chujmUc4T6fIBfXvDHFLIl2cZ0yvO4OwtAL0Ii0D5xhoIJwUWxxNo4kQ maeEHfTFVLrLlwgrdbm6zNpmXAOlOcr9P6cs8QvWuHUuWg==
S84BDVKNH5AGDSI7F5J0O3NPRHU0G7JQ.com. 86400 IN NSEC3 1 1 0 - S84CDVS9VPREADFD6KK7PDADH0M6IO8H  NS DS RRSIG
S84BDVKNH5AGDSI7F5J0O3NPRHU0G7JQ.com. 86400 IN RRSIG NSEC3 8 2 86400 20210109130004 20210102115004 31510 com. GDgkLbhWPJNCxdDcDKZRMcMh8TgEiTYuFmsX6cfpr7jpsdPT8neSmDKR 8fI4TnLl7ERhm5xRuKGEjgNsnz/8HNEFoXK/M7ES3RCPoYtYjbNj+k1Q 6kryy9nKNSiisYxg3dj8E2RwVZ2rZGOrNGPSi1yP2jx22qrswUAy1OqZ LNU+WNtgmB1SKL5f7JSt+lxzZrkduhSW8JGsArOOWF44XQ==
;; Received 840 bytes from 192.55.83.30#53(m.gtld-servers.net) in 8 ms

www.google.com.         300     IN      A       172.217.26.4
;; Received 59 bytes from 216.239.36.10#53(ns3.google.com) in 156 ms

kangetsu@ubuntu18:~

この通り, その他の設定は変えていませんが, Ubuntu 18.04 でも +trace が機能しました。

/etc/resolv.conf の変化はいつ入ったか

上記のような変更がいつ, なぜ入ったかについては, 以下の記事にて解説されていましたのでご参照ください。
https://kledgeb.blogspot.com/2016/06/ubuntu-1610-7-dnssystemd-resolved.html

systemd の issue での議論: 仕様だった

どうすれば動くかは分かったけど理屈はわからず, Web をさまよっていたら次の issue を見つけました。

https://github.com/systemd/systemd/issues/5897

一部引用します。

https://github.com/systemd/systemd/issues/5897#issue-226688715

... When the configured nameserver is systemd-resolved’s 127.0.0.53, systemd-resolved refuses this query.

$ dig +norecurse . NS ... Therefore, dig +trace does not work. ...

https://github.com/systemd/systemd/issues/5897#issuecomment-300238951

This is really by design. The DNS stub in resolved is not supposed to be a full DNS server. All it shall be able to do is reply to simply local lookups, i.e. RD lookups done by nss-dns or similar local implementations.

resolved is not a DNS server, and it's not going to become one. Sorry!

このように, systemd-resolved の仕様のようでした。
この後も discussion は続いていますが, 当分改修されることはなさそうです。

dig +trace を使いたい場合は, @ で nameserver を指定する必要があるようです。

おまけ

上では Ubuntu 16.04 の /etc/resolv.conf からフルリゾルバの IP アドレスを持ってきましたが, systemd-resolv --status から参照できます。
一番下の DNS Servers の IP アドレスが, 上で指定したものと一致しています。

$ systemd-resolve --status
Global
          DNSSEC NTA: 10.in-addr.arpa
                      16.172.in-addr.arpa
                      168.192.in-addr.arpa
                      17.172.in-addr.arpa
                      18.172.in-addr.arpa
                      19.172.in-addr.arpa
                      20.172.in-addr.arpa
                      21.172.in-addr.arpa
                      22.172.in-addr.arpa
                      23.172.in-addr.arpa
                      24.172.in-addr.arpa
                      25.172.in-addr.arpa
                      26.172.in-addr.arpa
                      27.172.in-addr.arpa
                      28.172.in-addr.arpa
                      29.172.in-addr.arpa
                      30.172.in-addr.arpa
                      31.172.in-addr.arpa
                      corp
                      d.f.ip6.arpa
                      home
                      internal
                      intranet
                      lan
                      local
                      private
                      test

Link 3 (enp0s8)
      Current Scopes: none
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no

Link 2 (enp0s3)
      Current Scopes: DNS
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
         DNS Servers: 192.168.3.1

今回は細かい理屈までは追い切れず消化不良ですが, いずれちゃんと調べておきたいです。

*1:RFC 8499 で用語の統一が提案されてはいます

『DNSがよくわかる教科書』疑問点まとめ1

DNS を体系的に勉強したことがなかったので, 『DNSがよくわかる教科書』を読みました。
名前にたがわず DNS がよくわかる本で, DNS の勉強をしたい人には本当にお勧めの本です。

まとめは別でやるかもしれませんが, ここではこの本を読んで自分が持った疑問と, 調べた回答をまとめておきます。

Q1. レジストリとレジストラの例は何があるか

ドメイン名の登録・運用・管理には レジストリ (Registry) とレジストラ (Registrar) が重要な存在 *1
名前がややこしいが, レジストリは TLD (Top Level Domain) ごとに存在する*2, ドメイン名を管理する機関。
レジストラは, 登録者からのドメイン名登録申請などを仲介してレジストリに取り次ぐ業者。

具体例:

レジストラによって扱っているドメイン名は異なる場合がある。
例えば 2021/1/1 現在, このブログの TLD .work は Google Domains では扱っていないのでそこにはドメイン移管できない。

移管可能な場合, または新たにドメインを登録する場合は, 複数存在するレジストラの中から, より自分に魅力的なサービスを提供しているものを選択できる。
例えば料金やサポート体制, 過去のインシデントやその対応を見て判断などできるので, レジストラ間には競争原理が働いている。

所感

短いですが年初なのでまずはこんなところで。
このブログのドメインを登録するとき, 国内だと「とりあえずお名前.com」という風潮があったと思っており, 私もあまり考えずにお名前.com で契約させてもらいました。

ただ, こうして見るとレジストラもいろいろあるので, 契約更新時にはもう少し調べて, 必要であればドメイン移管などもしてみたいですね。

今のところ個人的にはお名前.com には目立った不満はないです。
(強いて言えば, オプションを促すメールが毎日来ていて, かつそのメール配信停止の手段が面倒だったことと, 2020/6 にセキュリティインシデントが起きた*4 のが気にはなるので, 更新タイミングでは移管の検討もすると思います。)

*1:レジストリ・レジストラモデルを採用していない TLD もあるらしい

*2:必ずしも一対一対応ではなく, 例えば .com と .net はどちらも VeriSign が管理している

*3:JPRS が ピックアップして示した一覧 だけでも相当数ある

*4:ニュース記事としては https://www.security-next.com/115491 など

Ubuntu (Debian 系) パッケージ管理: apt 系コマンドの使い分け

本記事の目的

業務や勉強用の環境で Ubuntu を使っているのですが, 触り始めた当初混乱させられたのが「パッケージ管理」という概念と, 同じ目的で複数存在するコマンド体系でした。
私が調べた当時は, 「apt-get を使うのが普通」「apt-get はもう古いので aptitude を使う」「(aptitude などなかったかのように) apt-get は古い, これからは apt の利用を公式も推奨する」など, 様々な記述があったように思います。

その後なんとなくでコマンドを使い続けてきたので, 改めてドキュメントなどを読んで自分の中で整理しました。

なお, パッケージ管理の基礎については改めて記事を書く予定です (初めはこの記事に含めようとしたが, 長くなったのと目的がぼやけたので分けることにした)。

TL;DR

  • apt はあくまでエンドユーザーツール, バックエンドの apt-getapt-cache とできることは同じ
  • パッケージインストールやアップグレード: apt vs apt-get
    • 安定した互換性, 自動化のしやすさからサーバ運用などでは apt-get を使いたい
    • 個人利用なら apt は便利
  • パッケージ検索: apt vs apt-cache
    • apt-get と同じく, より安定しているのは apt-cache
    • ただしパッケージ検索などはインタラクティブに使うシーンがほとんどだと思うので便利な apt でよい
  • aptitudesynaptic はデフォルトインストールもされていないので考慮しない
  • 結論: サーバ運用スクリプトなどでは基本的に apt-get/apt-cache, 個人利用なら apt を使うと便利
  • 本記事の目的
  • TL;DR
  • 前提
    • 環境情報
    • 比較対象
  • apt について
    • パッケージ更新: apt vs apt-get
    • パッケージ検索: apt vs apt-cache
    • apt がどう便利か
  • まとめ
  • おまけ
続きを読む

Python において上書きされた配列の元の要素はどうなるか

 仕事や勉強はもちろんしているのですが, 最近遅々として記事が増えないので, もっと気楽なアウトプットを試してみようと思います。
第一弾として, C言語をかじった後に気になった, 表題の「Python において上書きされた配列の元の要素はどうなるか」の実験メモです。

なお, 言語実装によってメモリ管理の方法が異なります が, ここでは CPython を対象としています。

この記事で何を書くのか

 Python ではある配列変数を後で上書きすることができます。このとき, 上書きされた元の要素はどこからも参照されることのないデータになるはずです。
そこで, この記事では, このように Python で参照が切れたはずのメモリアドレスのデータがどうなっているのか を確かめます。

なぜこの記事を書くのか

 C言語と Python の配列の仕様の違いが気になったためです。

 C言語をちょっと勉強したのですが, C言語では Python のように, 既に宣言された配列変数を後から上書きすることは簡単にはできません。いくつか理由はあると思うのですが, 今の私がぱっと思いつくのは,

  • C言語は静的型付け言語であり, 配列もデータ型と要素数が宣言時に固定される
  • C言語の配列変数はポインタ

などです。

 C言語でできない配列の再代入ができる Python では, どういう挙動になっているのだろう, という疑問が発端です。

実験

環境情報

  • Python
$ python3 --version
Python 3.6.9
  • OS
$ uname -a
Linux ubuntu18 4.15.0-112-generic #113-Ubuntu SMP Thu Jul 9 23:41:39 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

道具

  • id(): 組み込み関数で, 引数として渡した変数のメモリアドレスを取得できる
  • ctypes: C と互換性のあるデータ型を提供するライブラリ。Python にポインタがないため疑似ポインタの実現に利用

手順

  1. 配列を宣言
    1. 配列とその要素のメモリアドレスを確認
    2. ポインタ経由で配列と代表の要素の値を確認
  2. 1 の配列に要素数と要素の値が異なる配列を再代入
    1. 配列とその要素のメモリアドレスを確認
    2. ポインタ経由で 1 の配列と代表の要素の値を確認
import ctypes

# 1
ls = [0, 1, 2, 3, 4]

## 1-1
print("ls is:", ls, "\nid(ls) is:", id(ls), "\nid of each element of ls is:", [id(i) for i in ls])

## 1-2
ls_address = id(ls)
ls_1_address = id(ls[1])

ls_address_pointer = ctypes.cast(ls_address, ctypes.py_object).value
ls_1_address_pointer = ctypes.cast(ls_1_address, ctypes.py_object).value

print(ls_address_pointer)
print(ls_1_address_pointer)

# 2
print("\nreassign ls...\n")

ls = [5, 6, 7]

# 2-1
print("ls is:", ls, "\nid(ls) is:", id(ls), "\nid of each element of ls is:", [id(i) for i in ls])

# 2-2
print("\nStill can access to the garbage address?\n")

print(ls_address_pointer)
print(ls_1_address_pointer)

結果

$ python3 pointer.py
ls is: [0, 1, 2, 3, 4]
id(ls) is: 140307991413192
id of each element of ls is: [10914464, 10914496, 10914528, 10914560, 10914592]
[0, 1, 2, 3, 4]
1

reassign ls...

ls is: [5, 6, 7]
id(ls) is: 140307991413256
id of each element of ls is: [10914624, 10914656, 10914688]

Still can access to the garbage address?

[0, 1, 2, 3, 4]
1

まず目につくのは, reassign ls... の後に ls 自信とその要素の id (メモリアドレス) が変化していることです。
つまり, Python では同じ変数に異なる値を再代入すると, まったく別のオブジェクトが作られる ことがわかります*1

そして, 元の配列がどうなったのかが, 次の Still can access to the garbage address? の後に示されています。
上書きしたはずの [0, 1, 2, 3, 4] という元のリストにアクセスできています。

Python では, こうした参照が切れた変数は, Garbage Collection によって回収されます。逆に言えば, GC が走るまでは, そのメモリアドレスにはデータが保持されており, 無理やりアクセスすれば読み出すことが可能となります。
とは言え本来は参照が切れた時点で GC が走るはずなので, 今回のように無理やり上書き前の変数のアドレスを確保して, ポインタでの参照のようなことをしない限り気にすることはないのだと思います。

なお, C言語では malloc() などでヒープに確保したデータは, 明示的に free() で解放しないとメモリリークを起こしてしまいます。

終わりに

 以上、「Python において上書きされた配列の元の要素はどうなるか」でした。
上書き前にメモリアドレスを保持しておいて, ctypes を使ってポインタ経由で読みだ出せばアクセスできましたね。

少しだけど C言語に触れたことで, Python などの理解も以前より進み, これからもより進む気がしています。

もともと自分の備忘も目的だったので, 今後しばらくはこれくらいの軽さで記事を書いていけたらと思います。

入門 Python 3

入門 Python 3

  • 作者:Bill Lubanovic
  • 発売日: 2015/12/01
  • メディア: 単行本(ソフトカバー)

*1:Python では配列も第一級オブジェクトです