Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Termuxでは日本語ツイートができなかったが、BIN/urlencodeでenv -i awkするとできるようになる #8

Closed
ghost opened this issue Jan 3, 2021 · 10 comments

Comments

@ghost
Copy link

ghost commented Jan 3, 2021

元々のものでは「statusが空文字列になってしまう」ことがBIN/tweet.shやBIN/twsrch.shなどで確認されていました(ASCII文字はそのまま)。
そこで-vxオプションによりバグの解析を行った際にurlencodeを行ったあとにstatusに該当する部分が消滅することが確認されました。
これに対しUTL/urlencodeの、最初のawkについて、env -i awkと編集することにより、urlencodeが意図通りに動き、日本語でのツイートができるようになります。

同様に、BIN/twtl.shでは日本語など、(おそらくASCII文字列ではない)ツイートについては文字化けされた状態で表示されますが、UTL/unescj.shのawkをenv -i awkで実行させることにより正しく表示されるようになります。

ロケールの変更を行ってもTermux上ではロケールの固定によりawkに0から255まで全てのバイトを出力させることができない こと、そして全ての環境変数を無効にした状態でならできることが原因なのではないかと思われます。

@321516
Copy link
Member

321516 commented Jan 4, 2021

それは環境変数の有無が原因ではない。GNU版の動きがおかしいのが問題なのだ。従って、busybox版を選択すれば正常に実行されるし、例え環境変数を無くしてもGNU版を選ぶ限り解決しない。

# 正しく動く
$ busybox awk 'BEGIN{ORS="";for(i=1;i<=255;i++)print(sprintf("%c",i));}' | od -A n -t x1

# 正しく動かない
$ env -i gawk 'BEGIN{ORS="";for(i=1;i<=255;i++)print(sprintf("%c",i));}' | od -A n -t x1

今の時点でわかっている対応方法は、シンボリックリンク awk の向いている先が gawk であるなら、それを busybox に向けるということだ。なお、Termux環境下であることを認識してgawkを回避するとか、あるいはTermux上のgawkの問題を解消するための方法はわかっていない。

@ghost
Copy link
Author

ghost commented Jan 5, 2021

それでも、awkがgawkへのシンボリックリンクで、かつgawkをenv -i awkとして呼び出すことにより全バイトが出力可能である、というのは奇妙でしょう。
gawkとして呼び出した場合でも同じことができるかを試してみましたが今のところ成功には至っていません。以下は試したことです。

-cないし--traditional
-v BINMODE=2(3もrwも不可)

追記 これをGAWK開発者にバグ報告しました。

@ghost
Copy link
Author

ghost commented Jan 8, 2021

助言ありがとうございます。
私の勉強不足であることをお教えいただいたことを感謝申し上げます。

env -i awkをTermuxで実行するとうまくいく原因が、次の通りであることがわかりました。

~ $ env -i awk --version
awk version 20190125
~ $ awk --version | sed 3q
GNU Awk 5.1.0, API: 3.0 (GNU MPFR 4.1.0, GNU MP 6.2.1)
Copyright (C) 1989, 1991-2020 Free Software Foundation.

~ $

余談ではございますが、sedは「これはGNUではない」と表示され、他の多くのコマンドはtoyboxのものであることがわかりました。

~ $ env -i sed --version
This is not GNU sed version 9.0
~ $ env -i toybox
acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
# 長いので中略
~ $ type toybox
bash: type: toybox: not found

@ghost ghost closed this as completed Jan 8, 2021
@ghost
Copy link
Author

ghost commented Jan 9, 2021

以前GNUに「バグ報告」をした際の返事は次の内容でした。

  • GAWKはargv[0]の内容を読むことはないため「プログラム名による挙動の違い」についてのバグ(と私が報告したもの)は「あり得ない」

  • ロケール変更を試すべし

  • -bを用いると「文字をバイト列として読ませる」ことを強制させることができる

この内-bが、Termuxのような「POSIXロケールが存在しない」環境でも有効であることが次の通り確認されました。

$ yes | head -n 255 | gawk -b '{printf"%c",NR;}' | od -A n -t x1 -v
 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10
 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20
 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30
 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40
 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50
 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60
 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70
 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80
 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90
 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0
 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0
 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0
 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0
 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0
 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0
 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff

@ghost ghost reopened this Jan 9, 2021
@321516
Copy link
Member

321516 commented Jan 14, 2021

-b オプションか、なるほど。しかし、他のAWKだった場合には知らないオプションが指定されたといってエラー扱いする恐れもあるから難しいところだな。環境変数を見て判断してくれればよいのだが。そもそも POSIXLY_CORRECT という環境変数がある時、GNU製コマンドはPOSIXに忠実な動作をするというルールがあるのになあ。

@ghost
Copy link
Author

ghost commented Jan 23, 2021

そこで、私は以前から次のように考えていました。

awkcmd=awk
case "$(
  gawk -b 'BEGIN{printf "%c",128}' 2>/dev/null |
  od -A n -t x1 -v |
  tr -Cd 0123456789AaBbCcDdEeFf
)" in (80) awkcmd='gawk -b';; esac

これこそがPOSIXロケールの有無に関係なく(GAWKについては)対応可能な方法だと考えました。
いかがでしょうか?

それからTermuxにはPOSIXロケールが提供されない情報の提供を怠り、大変申し訳ございません。

@321516
Copy link
Member

321516 commented Jan 23, 2021

なるほど、それなら次のように書けばもっと短くなるな。printfの結果が 0x80 になるかどうかを確認するというわけだ。また、等しいかどうかを確認するにもかかわらず == 演算子ではなく != 演算子を使っているが、それはawkとシェルでは0,1の真偽の意味が逆だからだ。

if gawk -b 'BEGIN{exit sprintf("%c",128)!="\200"}' 2>/dev/null
then awkcmd='gawk -b'; else awkcmd=awk; fi

だが、awkcmdというシェル変数にコマンド名を書き出すよりも、次のようにして alias 定義を追加する方がよいな。

gawk -b 'BEGIN{exit sprintf("%c",128)!="\200"}' 2>/dev/null && alias awk='gawk -b'

さらに言えば、提案されたコードはgawkがあればそれを積極的に選択する点が気にくわんな。次のようにして、どうしようもなければ仕方なく使うくらいの方がよいな。個人的には。

awk    'BEGIN{exit sprintf("%c",128)!="\302\200"}'             &&
awk -b 'BEGIN{exit sprintf("%c",128)!=    "\200"}' 2>/dev/null &&
alias awk='awk -b'

どう思うかな?

@ghost
Copy link
Author

ghost commented Jan 31, 2021

ご提案感謝申し上げます。しかし、UTF-8しか提供されない環境には対応しているのに対し、(存在するかは存じませんが)EUC-JPしか提供されない環境、その他のロケールしか提供されない環境などのことを考えれば逐次対応が必要になるかもしれません。
同様に世の中にはGAWK以外も存在するでしょう。

最早いっそのことすべてのバイナリをawk '{…;printf "\\\\ooo";…}' | xargs -I x printf x(すなわちバックスラッシュと三桁の八進数)で出力させる、という手法が確実でしょう。これならばメモリの無駄遣いにはなりますが、変なワークアラウンドなしでどの環境でも上手くできるでしょう。

追記。
失礼しました、awk配列内ではどうするのかということが欠けていました。

@321516
Copy link
Member

321516 commented Feb 1, 2021

うむ、UTF-8エンコーディング以外を採用したロケールのことは想像していなかったな。そこで、我が知っている全てのロケールを指定した場合のバイトパターンを観察してみた。たくさんのロケールをサポートしている環境(例えばCentOS)で次のワンライナーを試してみよ。(ただし、"awk"のところは、環境によっては"gawk"と書き換える)

$ locale -a | while read l; do printf '%-21s' "$l"; LC_ALL=$l awk 'BEGIN{printf("%c",128)}' | od -A n -t x1; done | sort -bk 2

これを見ると、返されるバイトパターンは次の3種類であることがわかる。

  1. 80 (Shift JISやEUC-JPも含む、UTF-8エンコーディング以外を採用するロケールの大半)
  2. C2 80 (UTF-8エンコーディングを採用するロケール)
  3. 81 30 81 30GB 18030というエンコーディングを採用した中国のロケール"zh_CN.gb18030")

3番目のもの。これこそが、1バイトでもなく、UTF-8でもない、第3のバイトパターンというわけだな。我が前に提案した判別コードは1バイトかUTF-8かの2パターンの判別にしか対応していなかった。指摘してくれたおかげでこの問題点に気づけた。

ならば、この判別コードでよいのではないか?

awk    'BEGIN{exit sprintf("%c",128)=="\200"}'             &&
awk -b 'BEGIN{exit sprintf("%c",128)!="\200"}' 2>/dev/null &&
alias awk='awk -b'

C2 80 が返ってくることを想定するのではなく、80 が返ってこない場合に -b オプションを付けてみて、それで 80 が返ってくるようになるのであれば -b オプション付きのエイリアスを定義するというわけだ。これならどうだ。

なお、gawk以外の実装、つまり -b オプションでは直らん可能性のある実装を想定してデータを全て3桁8進数( \ooo 形式)で取り扱うという案は採用できんな。パフォーマンスの犠牲の大きさに対して救済できる環境が一体どれだけあるのか未知数だ。ここはUNIX哲学の「90%の解決」をわきまえるべき場面だと考える。

@ghost
Copy link
Author

ghost commented Feb 22, 2021

ご提案ありがとうございます。しかし、「Cロケールが提供されている、またはインストール可能である」という時点で既に90%の解です。これに対して今のところ「export LC_ALL=Cが意味を為さない」10%は恐らくTermuxだけです。「90%の解法」でよろしいのであれば、「export LC_ALL=C」で十分でしょう。また、ロケール依存があるawkはおそらくGNU製のものだけであり、それについてもロケール設定で90%は解決します。

今回のissueにおいて主な解決策を最初に自ら示したのに対し、未知の環境に対する不要不急の解決策の模索をさせてしまい、また、貴重なお時間を奪ってしまい、誠に申し訳ございませんでした。このissueを、本来あるべき姿から大幅に逸脱させてしまったことを謝罪申し上げます。

@ghost ghost closed this as completed Feb 22, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant