それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

bashのalias に引数を渡すには?

Bash のAliasに引数を渡す。

何も考えずに、Aliasをするとそのまま渡される。

alias some=echo
some aaaaaaa

alias は実行前に、bashが解釈するために some aaaaaaaecho aaaaaaa` に展開される。

出来ない。

なので次のようなことは出来ない。

alias git-push-to='git push "$@" master'

これは、bash の実行前に git-push-to remoteA git push "" master remoteA と展開されてしまう。

展開順について。

先の例は、展開が次のようになる。

git-push-to remoteA 

これは、次のように展開。

git push "" master remoteA 

引数は受け取れない。

引数は受け取れない。なぜなら、単純な置換がされるだけだから。

bashのコマンド引数を順番を変えて、aliasに渡すには?

alias でなく関数を使う

まぁ頻出の話題なので、これくらいは基本ですよねってことで。

alias fooo="/path/to/cmd $@ -opts" は出来ない

alias fooo="/path/to/cmd --opts $@ " # できる
alias fooo="/path/to/cmd $@ --opts" # 出来ない
funciton fooo (){ /path/to/cmd $@  --opts } # 出来る

というわけで、alias だとoptionsと引数の順番を指定できないのです。

代わりにFunctionを使うって話です。

引数はと$@ 使っておけば大丈夫です。

  • $@ 引数全部
  • $1 一個目
  • $2 二個目
  • $3 三個目

のように引数を渡せる。

function はbashrcに

functionは ~/.bashrc などに記述しておくといいでしょう。

echo function foo {} >> ~/.bashrc

もし.bashrc をデフォルトから汚したくない!と思う人は(まれでしょうが)

echo source .bashrc.takuya >> .bashrc
echo function foo {} >> ~/.bashrc.takuya

などと、自前の.bashrcをロードする設定を.bashrcに書いておけば解決ですね。

若しくはコマンドファイルを作る

関数を作る以外には、「コマンド」を作るというのが太古から習わしです。

touch fooo
chomod +x fooo
vim fooo
mv fooo ~/takuya/.bin
export PATH=~/takuya/bin:$PATH

コマンドファイルを作って、PATHを通す。

fooo

コマンドファイルには次のように記述します。

#!/usr/bin/env bash

/path/to/cmd $@ 

記述することはfunciton と同じです。

ファイルを作ると管理が面倒?

一番手っ取り早いのでファイルを作る手段も知っておくと便利です。

function とファイルをどちらを使うべきか

好きな方でいいです。管理の容易さ、一覧性などを考えて好きな方で良いと思います。

ただし、zsh/bash などを使い分ける場合や、ユーザーを切り替えてsu/sudo する場合は「ファイルの方がいいと思います」

function は個人専用だったりシェル・スクリプト中で使うものだと思います。

あれ?これってalias?function?

あれれ?このコマンドってalias のfunciton なの?それともコマンドファイル?

aliasを作りまくってるとわからなくなるので

  • hash
  • type

で確認します。

hash / type の例

type で現在のコマンドがなにか分かります。hash でそのコマンドがどのようにハッシュ(キャッシュ)されているかが分かります。

hash
takuya@~ $ hash
hits    command
   1    /usr/local/bin/rm-trash-by-takuya
   0    /bin/rm
type
takuya@ ~ $ type rm
rm は `rm-trash-by-takuya' のエイリアスです

bashはコマンドをなんどもPATH探索しなくてもいいようにhashテーブルに読み込んでいます。

追記

正確には shopt -s expand_aliasesしているときの動作なのだが、expand_aliasesなど今更無効にできるわけもなく

過去資料

bash(csh)のhashとか言う、気づかないけど便利な機能 - それマグで!

シェルにコマンドが存在するか調べる - それマグで!

更新

2021-06-04 更新。

Chrome のパスワードがOSX Keychains に保存・同期しなくなった

chrome がkeychainsに保存しなくなった。

数カ月前から、KeychainsにWEBログインパスワードが存在しないな、、おかしいなと思ってた。

そのうち調べようと思って、ようやく調べて、驚愕した

OSX 版 Chrome は Keychains にパスワードを保存できなくなった

とのこと。

最初なんでこんなことになったのか理解できなかったが、どうやら、OSXのアップデートによってKeychainsにアクセスできるのが、AppStore経由のAppのみになったらしい

ということで、AppStore経由してないものはSafariとパスワードを共有できなくなった。

そのため、もう面倒だし、Keychainをサポートを外してGoogle Chromeは内部にキーチェーンを保存することになったらしい。

えええ、不便だ・・・不便だよ

変更はicloudキーチェーンに関するものだけだろうに、キーチェーンごとバッサリカットですか。。。

細かい話

Safariは保存済パスワードがiOS/OSXで同期するために、iCloud キーチェーンを使う。

iCloudキーチェーンはApp Storeのアプリからは使える。Chromeは非App Storeなので無理。

問題はiCloudキーチェーンの感じ。

ただし、iCloudキーチェーンを使ってない場合はChromeに保存可能なはずだが、キーチェーン関連の機能をChrome側がバッサリカット(←私ここで影響でた)

Firefoxなども同様。

こればっかりはApple vs Googleの関係性も相まってもう解決しないと思われる。

というか、すでにローカル・キーチェーンにあるパスワードを一切参照しなくなったのは辛い。

私も、パスワードの管理方法を変える必要が出てきた。

参考資料

Issue 466638 - chromium - Remove OS X Keychain integration for saved passwords - An open-source project to help move the web forward. - Google Project Hosting

Knowledge for Google Products: 【備忘録】Chroem45のMac用パスワード管理方法が変更(Macキーチェーン→Chrome内部へ)

https://productforums.google.com/forum/?hl=ja#!topic/chrome-ja/9K4rvWBGkpc;context-place=forum/chrome-ja

OSX のMACアドレスをランダムに切替コマンド

OSXMACアドレスを手軽にランダマイズ

無線LANとか、ランダムなMACアドレスで認証してMACアドレス認証が有効になってるかチェックしたいことがあります。

MACアドレスで認証が有効に働いているかは、MACアドレスをランダムに変更してチェックするのが手軽だと思いました。

MACアドレスっぽい乱数をopenssl で作る

openssl で乱数を使ってMACアドレスに許容される乱数を作ります。

openssl rand -hex 6 | sed 's/\(..\)/\1:/g; s/.$//'

コマンド

gist81cdef93ff867e7686e7

2015/12/31追加 単純な乱数だとエラー

単純な乱数だと、MACアドレスとして正しくないのか、設定できないことが頻発した。そのため設定失敗したら、リトライするように書き換えた。

また、コマンドオプションとソースを整理してみた。

ほかに、極力パスワード再確認が減るように実行コマンドを見なおした。

2021-01-27

誤字修正

2022-04-20 追記

macbook で T2 搭載(2018 mbp 以降 ) では、MACアドレスというか、そのもとになるWiFiバイスが、OS管理下にないのでMAC-Addressの書き換えはできません。

かといって、iOSみたいに、WiFi接続時のMacアドレス匿名化もできません。中途半端でAppleが無視のバグです。諦めるしかありません。

curl とxpath でお手軽スクレイピング

この記事は [クローラー/Webスクレイピング Advent Calendar 2015] の一つとして書きました。

公開遅いけど。ごめんね

この記事の目標

curl コマンドの使い方を覚えつつ、スクレイピングをやっていきます。

この記事で紹介すること

用意するもの

知っておくと便利な知識

js への対応

基本方針は「JSに対応しない」

だって、リクエストヘッダ見てたらわかるもん。

curl コマンドでWEBページを取得する

スクレーパをするまえに curl コマンドを紹介します。

curl コマンドは libcurl をコマンドライン使うものです。 curl コマンドはコンパイルオプション次第で http2 / scp / ssh / ftp などほとんどのファイル転送に対応可能です。 おもにHTTPdサーバー への アクセスするのに使います。

curl コマンドを覚えよう

curl コマンドの基本

curl http://qiita.com

curl コマンドで HTTP HEADを見る

curl -I http://qiita.com

curl コマンドでHTTP 302/301 に追従する

curl -L http://qiita.com

 curl で特定のデータを保存する

curl -L http://qiita.com > out.html

 ファイルを保存

curl http://cdn.qiita.com/assets/siteid-reverse-9b38e297bbd020380feed99b444c6202.png > out.png

 URLのファイル名で保存

curl -O https://i.gyazo.com/f609d81c30b580c9015a890643ecc604.png

 サーバーとのやりとりを詳細に表示

curl -v -L http://qiita.com > out.html

 進捗率の代わりにプログレスバーを表示

curl  -#  -O URL

 プログレスバーを一切非表示

curl -s  URL

これくらいを覚えておけば、ほとんどの場合に対応できます。

なぜcurl なのか?

スクレーパーなのになぜcurl のお話をしているのかというと、スクレイピングを作る上で curl は不可欠なツールなのです。

欲しいコンテンツがメインディシュとしたら、ブラウザはレストラン。プログラム言語はコンビニ、curl は「お箸・フォーク」です。美味しくいただくために不可欠なツールです。ちなみに xpathは取り皿=食器ですね。

curl を用いたスクレイピング

curl+ grep でサイトの情報を取り出す。

基本中の基本です。

curl https://qiita.com | grep  title

しかし結果が、、汚い・・・美しくない。

takuya@~/Desktop$ curl -s  https://qiita.com | grep  '<title>(.+)</title>'
1:<!DOCTYPE html><html xmlns:og="http://ogp.me/ns#"><head><meta charset="UTF-8" /><title>Qiita - プログラマの技術情報共有サービス</title><meta content="width=device-width,initial-scale=1" name="viewport" /><meta content="Qiitaは、プログラマのための技術情報共有サービスです。 プログラミングに関するTips、ノウハウ、メモを簡単に記録 &amp;amp; 公開することができます。" name="description" /><meta content="summary" name="twitter:card" /><meta content="@Qiita" name="t(いかりゃく

curl + grep でサイトの情報を綺麗に取り出す

grep -o オプションを使う

curl  https://qiita.com | grep -o '<title>(.+)</title>'

結果はほら綺麗。

$ curl -s  https://qiita.com | grep -o '<title>(.+)</title>'
<title>Qiita - プログラマの技術情報共有サービス</title>

curl + grep -o でそこそこ便利になりますね。

curl + m5sum 

取得した内容をmd5sum にかける

curl -s   http://takuya-1st.hatenablog.jp/entries/2015/12/11 | md5sum

これにより取得した内容が同じかどうか検出が可能になる。

脚注 last-modified や e-tag を使うべきなんだろうが、昔から糞みたいなブログ実装が多くて304 Not Modified を返さないサイトが多すぎるんですよ。むかしから妙竹林の代表例はCA

curl + md5sum + mail 

サイトに更新があったら通知する。シェルスクリプト

curl + md5sum で更新監視なら、3分で書けるよ。カップ麺待ってる間にできちゃうね。

url="http://localhost/"
digest=`curl -s  $url  | md5sum `


while true ;  do
  current=`curl -s  $url  | md5sum `
  if [[ $digest != $current ]] ; then


    echo changed!!
    sendmail ほげほげ
    digest=$current
  fi
  sleep 1
done

サイトが変化したら、MD5の結果が変わるので、その結果を見つけて通知します。

これだと毎秒見に行ってます。むかしブログでJRの運行情報を取得した話を書いた時に「30秒に1度は病的なアクセス頻度」と言われたことがある。クローラーを避けたい管理者はキャッシュを正しく扱ってください。クローラーは違法行為でも攻撃でもありません。「Last-modified-since/ If-none-match」に正しく応答してください。HTTPのキャッシュすら扱えないSI'erはWEB案件にくんな。毎分クローラーを走らせたくらいで攻撃だとメール送ってくる関西電力あんたのことだ。おっと。

ユーザーエージェントを変更する

いくつかのサイトは、ユーザーエージェントによる識別をしているので、私のブラウザの代理をcurl にやらせるので、ユーザーエージェントをブラウザに合わせておく

curl --user-agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.48 Safari/537.36" http://www.yahoo.co.jp/

ブラウザのリクエストをCurlでやる

curl にはいろいろと便利な機能があります。 基本的にhttp リクエストは curl で作成できます。

例えば、ブラウザのリクエストと全く同じリクエストを再現するにはChromeの開発ツールでcURLとしてコピーすればとても簡単です。

f:id:takuya_1st:20151225211705p:plain:w500

chrome からコピーしてきてシェルに貼り付け

ChromeからcURL コマンドとしてコピーしてシェルに貼り付けたら「簡単に」リクエストを再現できる。

Cookieやヘッダもそのまま埋まってくるので、スクレーパー作る時は、Chromeのコピーから始めると便利。

curlCookieの取り扱い

curlcookieを永続したいです。

WEBページにアクセスするにはほとんどの場合、Cookieによって識別されます。

cookie 保存には -c オプション

curl -c ${保存したいパス} http://www.yahoo.co.jp

Cookieの再利用 には -b オプション

curl -b ${保存済みパス} http://auctions.yahoo.co.jp/

Cookieを前回のセッションから再利用しつつ、次回のために保存

curl -b  path -c path  http://auctions.yahoo.co.jp/

オプションの -b -c を同時に使います。ファイル名は同じで構いません

注意:path に同じものを記述するかといって省略することはできません。

curl -bc path  http://auctions.yahoo.co.jp/ ## これはできない。Cookie使えない

これで、Cookieの問題を気にせずに扱えるようになります。

期限なしのセッションクッキーも保存されます。

オプション -c を使えばsession-cookie も問題なく保存されます。

もし保存したくない時は -j をつけてください。

注意:期限なしとは期限の設定がされてないCookieのことです。「期限なし」を長期間Cookie(通称:永続Cookie)と勘違いしそうですが、期限設定なし=セッションCookie=ページを閉じるまで有効=Windowを閉じるまでです。(=タブを閉じただけでは消えない)

Cookieをどこから取り出すのか。

でも、Form送信してCookie作るのめんどくさい。

Cookieは前述のChrome開発ツールの右クリックから取り出すほか、Chromeのユーザープロファイルから取り出すこととができます。

わたしはChromeからコマンドで取り出すことが多いです。

https://github.com/takuya/chrome-storage

chrome-cookie .yahoo.co.jp | jq .

本題のスクレイピングです。

さてさて、それでは準備も整ったしスクレイピングを始めていきたいと思います。

ここまでで

という武器を一通り揃えました。

スクレイピングするときにもう一つ不可欠な武器があります。それがlibxmlです。

最後の武器 libxml

スクレイピングをする時に欠かせない最終兵器がlibxml です。

libxml に添付のxpath 用コマンドでページ要素を取得

grep では絶対に足りなくなるので、 libxml でXMLを解析が必要です。

HTML も XML として解釈してもらえるのでlibxml はスクレイピングには欠かせない。

ruby nokogiri や python lxml なんかそうですね。

libxml はコマンドからも使えるのです。

 libxmlのxmllintコマンドでxpath を実行する

xmllint コマンドではxpath を実行できます。便利!

xmllint --xpath "//nodename" sample.xml

インストール

sudo apt install libxml2-utils

xmllint コマンドでhtml を扱う

xmllint で html を扱うにはhtml オプションをつけます

xmllint --html --xpath "//head/title" sample.html

タイプ量が面倒なので xpath でalias しておきます。

alias xpath="xmllint --html --xpath 2>/dev/null"

エラーメッセージのゴミ箱行きは、まぁ説明のためです。 xpath の基本構文はあとで詳しく書くとして、xpath でどんどんページを取っていきます。

xpathcurl コマンドと組み合わせて戦います。

curl -s  'http://www.yahoo.co.jp/'  |  xpath "//head/title"  - 

3つの武器が揃った

スクレイピングに欠かせない、3種の神器をcurl + libxml で整えました。

処理 コマンド  
ファイル取得 curl  
cookie 取り扱い curl -b path -c path
HTML 解析 xmllint --html --xpath

それではスクレイピング処理をします。

前置き長すぎ。疲れた。

連続ページ取得をしていきたいと思います。

例えば、yahoo オークションの検索結果ページからリンクを全て抜き出すには

curl -s -L  http://j.mp/1YC5mSM | xpath   "//h3/a/@href"  -

この結果それぞれに対して、ページの詳細を取得して保存する。

curl -s -L  http://j.mp/1YC5mSM | xpath   "//h3/a/@href"  -
さらにxargs で展開して

取得したhref の一覧を、さらにxargs で展開して詳細ページにアクセスします。

curl -s -L  http://j.mp/1YC5mSM |xpath   "//h3/a/@href"  -  ¥
| sed 's/href=//g'¥
| sed 's/"//g'  |¥
xargs -P0  -d ' ' -I@  curl -v  -O -L @
間に挟まるsed が邪魔なので

自作のxpath 関数に書き変えます。

git clone git@gist.github.com:894c5aeabc620344bcea.git
cd 894c5aeabc620344bcea
cp xpath /usr/local/bin/
chmod +x /usr/local/bin/xpath
さらに省略化
curl -s -L  http://j.mp/1YC5mSM | xpath "//h3/a/@href"  | xpath "//h1/text()"

xpathcurl の組み合わせでそこそこ戦える。

curlxpath ぱぱっとデータ取得

anemone つかえって話なんだろうけど。

selenium ドライバ使えばいいんだろうけど。

ページの解析もシェルでてきた方がタイプ量少なくて便利だよね!!!

もう少し続くんじゃ。

続き→curl+xpath から始めるお手軽スクレイピング(2) - それマグで!

関連資料

grep でマッチした部分だけを取り出す http://takuya-1st.hatenablog.jp/entry/20121112/1352750670

xpath コマンド http://takuya-1st.hatenablog.jp/entry/2014/08/24/031832

2021-11-28

apt install libxml2-utils を追記

openssl の暗号化と同等のrubyでの処理

openssl で暗号化すると便利なんだけど

暗号化をおこなったデータをそのままプログラムを経由して読みたいよね。やっぱり。

openssl で暗号化 → ruby で復号化

openssl enc -e  -aes-256-cbc -salt -in test.json -out enc.json -pass password:my_pass

これで暗号化したファイルを、rubyでデコードしてみる

      passphrase = "my_pass"
      data = open("enc.json", "r").read
      data = data.force_encoding("ASCII-8BIT")
      salt = data[8,8]
      data = data[16, data.size]
      cipher = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
      cipher.decrypt
      cipher.pkcs5_keyivgen(passphrase, salt, 1 )
      data = cipher.update(data) + cipher.final
      open("out.json", "w"){|f| f.write data }

ruby で暗号化 → openssl で復号化

ruby で暗号化したものを openssl で復号化する。

      passphrase = "秘密秘密"
      data = open("plain.json", "r").read
      cipher = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
      salt = OpenSSL::Random.random_bytes(8)
      cipher.encrypt
      cipher.pkcs5_keyivgen(passphrase, salt, 1 )
      data = cipher.update(data) + cipher.final
      ## salted
      data = "Salted__" + salt + data
      open("enc.json", "w"){|f| f.write data }

openssl で復号化

openssl enc -d  -aes-256-cbc -salt -in enc.json -out plain.json -pass password:秘密秘密

OpenSSLで簡単にファイルを暗号化・復号化する(コマンド一発)

openssl コマンドでファイルを暗号化します。

ファイルを暗号化してパスワードをつけたい場合、一番お手軽なのはWindowsExcelのパスワードですが、 一般のファイルで、ファイルにパスワードをつけたい場合は、opensslをコマンドから使うのが手軽です。

openssl コマンドでファイルを容易く暗号化出来る

ファイルを暗号化するには、とてもシンプルにopenssl を使うのが楽ですよね

ファイルの暗号化

次の例では、ファイル(test.json)を暗号化しています。 ここでは、パスワード($PLAIN_TEXT_PASS)を鍵として使います。

openssl enc -e  -aes-256-cbc -salt -pbkdf2 -pass pass:$PLAIN_TEXT_PASS -in test.json  -out test.json.enc

ファイルの復号化

次の例では、ファイル(test.json)を復号化しています。

openssl enc -d  -aes-256-cbc -salt -pbkdf2 -pass pass:$PLAIN_TEXT_PASS -in test.json.enc -out test.json

openssl enc を使う

ファイルをAESで暗号化するには enc を使う。

openssl enc  オプション

じつは、openssl はサブコマンド方式なので、次のようになっています。enc はサブコマンドの一つです。

openssl サブコマンド オプション

オプションの解説

引数 意味
enc 対象鍵(パスフレーズ)で暗号化・復号化
-e 暗号化処理を指定
-d 復号化処理を指定
-salt slatを使うことでバイナリからの憶測を困難に
-pass パスフレーズをどこから取るか (指定なしはプロンプト )

パスフレーズの与え方

  • pass:password プレインテキストで直書く
  • env:var 環境変数から
  • file:pathname ファイルから
  • fd:number ファイルディスクリプタから
  • stdin 標準入力から
パスフレーズの与え方の例

よく使いそうなのは次の3つでしょうね。

プレインテキストで

openssl enc -e  -aes-256-cbc -salt -pass pass:秘密秘密

環境変数から

export my_pass=秘密秘密
openssl enc -e  -aes-256-cbc -salt -pass env:my_pass

ファイルから

echo  秘密秘密 > my_pass_file
openssl enc -e  -aes-256-cbc -salt -pass file:my_pass_file

詳しくは、openssl のPASSWORD ARGUMENTSの章を参考にすること

2018-11-26

検索キーワードにうまくマッチしないのでエントリ書き換え、。

2020-12-16

openssl が -pbkdf2 をつけないと deprecated というので追加した。

参考資料

  • man enc
  • man openssl

みんな大好き!man ページ

download-pdf

download-pdf

クレジットカードの入力を実現する簡単なブックマークレット

クレジットカードの番号の入力がめんどくさい

クレジットカード番号を入力が4つの「Input」に分割されててイライラしませんか?私はイライラします。

4回もコピーしなおしとかちょっと無理

f:id:takuya_1st:20151208173502p:plain

作った

作った作った。面倒なことは自動化する。

ブックマークに登録(動作サンプル)

- - -

クレジットカードカンタン入力

iterateNext()で

input 書き換えながらiterateNextするとエラーになった。 xpath で検索した結果を使うときは動的にノードの状態が変わるとダメなようだ・・・不便ね。

Uncaught DOMException: Failed to execute 'iterateNext' on 'XPathResult': The document has mutated since the result was returned.

2019-07-06 更新 2019-08-21 更に更新

.//input だと取れすぎるので、Xpathを修正する。

(function() {
    card = []
    card_number = window.prompt("カードナンバー?", "");
    if (card_number.length == 16) {
        card_chars = card_number.split("")
        card.push(card_chars.slice(0, 4).join(""))
        card.push(card_chars.slice(4, 8).join(""))
        card.push(card_chars.slice(8, 12).join(""))
        card.push(card_chars.slice(12, 16).join(""))
    } else if (card_number.length == 19) {
        card = card_number.split("-")
    }
    card_input = []
    var xpath = '//*[count(./input[@type="text"])>=4]//input '
    +' | //*[count(.//input[@maxlength=4])=4]//input'
    +' | //*[count(.//input[@size=4])=4]//input'
    +' | //*[count(./input[contains(translate(./@name,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz"),"card") and @type="text" ])>=4]/input'
    +' | //*[count(./input[contains(translate(./@id,  "ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz"),"card") and @type="text" ])>=4]/input'
    /*ret = document.evaluate("//*[ count(./input) = 4 ]//input", document)*/
    ret = document.evaluate(xpath, document)
    
    while ((input = ret.iterateNext())) {
        card_input.push(input)
    }
    card.forEach( (e,i) => {
      card_input[i].value = card[i]
    });
})()

bashでファイルがリンクかどうか調べる

このファイルがリンクかどうか調べるには

if  [  -f /usr/local/bin/ruby -a  -L /usr/local/bin/ruby ] ; then
  echo "link exists"
else
 echo "not link"
fi

Bashの比較オプション

オプション 意味
-f ファイルかどうか調べる
-L シンボリックリンクかどうか調べる

リンクかどうか調べるには、ファイルの存在を調べたうえで、リンク状態を調べる

ファイルの存在は必須ではないかもしれない。でもリンクかどうか調べるときはリンクを取得したい、リンクを作成したいの場合がほとんどなのでファイルの存在を調べないとはまる。

参考資料

http://tldp.org/LDP/abs/html/fto.html

nokogiriがインストール出来ないとかいう定番のアレ Mac OSX 10.11.1

nokogiri のインストールに失敗するというミス

これが有名なNokogiri地獄だ!まさか自分がハマるとは思わなかったですね。

Nokogiriとかいう多くのRubyGems ライブラリで使われるものがインストール出来なくて詰むという。

とっても悲しい事象が発生しました。

rails, mechanize → nokogiri 依存 → mini_portile2 依存 で詰む。何処でコケたか考えることすら面倒な感じです

解決方法は「ググらない?」

ググッても、「私はコレでできました」「ウチではこのコンパイルオプションで動きました」とか皆行ってることがバラバラなんですね。nokogiri のインストール出来ませんのブログも、 homebrew も rbenv も入ってるのか、Xcodeのバージョンも記載が無かったり闇を感じる。2時間ほど検索して時間を無駄にした。来年OSX 10.12(OSXi 11 )の自分のためにメモっておく。

nokogiriのインストールは

今日のnokogiriはいくつか改善されている。

OSX の標準添付のruby ( /usr/bin/ruby ) を使う限りでは、次の通りで簡単にインストール出来る

sudo /usr/bin/gem install nokogiri -- --use-system-libararies

若しくはinstall_dir を指定して自分のgem環境にnokogiriを入れられる。

mkdir ~/.lib/ruby
/usr/bin/gem install --install-dir ~/.lib/gems nokogiri -- --use-system-libararies

随分と楽になったね。でも私は、homebre+rbenv+ruby-build環境なんだ。

OSX にnokogiriを入れなおしたのでメモを取る。

  • 環境の確認
  • homebrew の確認
  • rbenv の確認
  • 環境をOSX標準に近づける
  • libxml2 の確認
  • rbenv で新規環境
  • nokogiri インストール

この順番で処理をしていった。

OSXの環境の確認

まずはじめに、OSXの環境の確認。今の私のOSXは最新バージョンに自動アップデートされている。

takuya@~/Desktop$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.1
BuildVersion:   15B42

ビルド環境の確認

次にビルド環境の確認をしておく。

takuya@~/Desktop$ brew --config
HOMEBREW_VERSION: 0.9.5
ORIGIN: https://github.com/Homebrew/homebrew.git
HEAD: ae6c42c7eacf5196e83098658b795c883b9d9a87
Last commit: 9 hours ago
HOMEBREW_PREFIX: /usr/local
HOMEBREW_REPOSITORY: /usr/local
HOMEBREW_CELLAR: /usr/local/Cellar
HOMEBREW_BOTTLE_DOMAIN: https://homebrew.bintray.com
CPU: quad-core 64-bit haswell
OS X: 10.11.1-x86_64
Xcode: 7.1.1
CLT: N/A
Clang: 7.0 build 700
X11: 2.7.8 => /opt/X11
System Ruby: 2.0.0-p645
Perl: /usr/bin/perl
Python: /usr/local/bin/python => /usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/bin/python2.7
Ruby: /usr/bin/ruby => /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby
Java: 1.8.0_31, 1.8.0_25, 1.7.0_75, 1.6.0_65-b14-468

Xcode が7.11 ですねー

大好きなgccがなくてLLVMです。

rbenv の確認

念のためrbenvも確認しておきます。

takuya@~/Desktop$ rbenv --version
rbenv 0.4.0
-環境をOSX標準に近づける

nokogiriのメンテナーがコンパイルチェックしてるだろう標準環境に戻します。

OSXの標準のつもりな私の環境も、もしかしたらおかしくなってるかもしれない。

brew の一旦退避する。

homebrew や rbenv が入ってると、ミスがわからないので全部を片付けておきます。後で戻すけど

brew list | xargs -P5 -I@ brew unlink @

これで /usr/local/{bin,lib,include,opt} をほぼ空っぽにしてしてく。

brew コマンドだけ必要であとはいらない。

さらに、念のため、独自設定だらけのbashrcも退避しておいた。

私の場合、bashrcに.gemsフォルダ参照や、alias rubyだのrbenv ショートカット設定だのが多く入ってるので邪魔になりました。普通はやらないでいいとおもう。

mv ~/.bashrc ~/.bashrc.bak
さらに、ログインシェルを標準/bin/bashにする。

zsh や /usr/local/bin/bashを使ってると切り分けが面倒なので、出来る限り初期のクリーン・インストール済み環境に近づけた。

f:id:takuya_1st:20151207110034p:plain:w300

で、ほぼ素のbashでターミナルを起動しておく。

ここままでで、最低限必要な物だけを戻しておく

brew link ruby-build rbenv
無い
  • /usr/local/bin/{bin,lib} | brew unlink でbrew 関係はすべてリンクから消した
  • ~/.gems | bashrcを無効にして、gem ,ruby_lib もまっさらに
  • ~/.rbenv | こちらも退避した
ある
  • /usr/local/bin/brew
  • /usr/local/bin/rbenv
  • /usr/local/bin/ruby-build

最低限を残しbrewもほぼ初期状態。

面倒だったら新規ユーザーでどうぞ

無味乾燥な環境を作った

f:id:takuya_1st:20151207110103p:plain:w300

ほぼ素の状態のbashでターミナルを使うことにした。これで余計なことに神経を使わなくて済む。

おもむろに、nokogiri (system-ruby)の ビルドを試してみる。

ほぼ、素の状態なのでOSXのsystem ruby で nokogiriを入れてみる

gem install だけだとPermission エラーになるので自分のホームディレクトリにコンパイルして格納

gem install   nokogiri --install-dir ~/.lib/gems  --verbose -- --use-system-libaries

これで、まず問題なく動くことがわかる。

余計なものは一切ない。nokogiriの製作者が意図した環境でメンテナーがテストした環境になってると思う。

とりあえず、rbenvを作ってみる。

私の環境ではrbenvもコケてたので入れなおしてみる。 2.2.x 系はすでに動いているので、とりあえず2.1.x系で実験

RUBY_BUILD_CACHE_PATH=/tmp rbenv install -v 2.1.7
(ry
Installed ruby-2.1.7 to /Users/takuya/.rbenv/versions/2.1.7

無事インストールが動き出した。

brew のリンクを元に戻す

ここままでで、環境の確認が出来たので、おもむろにbrew linkを戻して環境を整理しておく。

brew list | xargs -P5 -I@ brew link @
libxml系をリンクする

あとで必要になるので、libxml2 の最新版を入れておく

brew install libxml2 libxslt 
brew --force libxml2 libxslt 
libxmlのバージョン確認

libxml2 をリンクした状態でバージョン確認をしたらこんな感じ

takuya@~$ xml2-config --version
2.9.2
takuya@~$ xslt-config --version
1.1.28
takuya@~$ which xml2-config
/usr/bin/xml2-config
takuya@~$ which xslt-config
/usr/bin/xslt-config

最新版の nokogiri 1.6.7 で必要になるみたい

rbenv を作る

rbenv をターゲット環境で作ってみる。

RUBY_BUILD_CACHE_PATH=/tmp rbenv install -v 2.2.3
(ry
Installed ruby-2.1.7 to /Users/takuya/.rbenv/versions/2.2.3

で、rbenv 経由で ruby 2.2.3 を有効に

rbenv global 2.2.3 
nokogiri (rbenv 2.2.3)をインストールしてみる。

で、やっとここまで到達。ruby 2.2.3 (rbenv) に nokogiri 1.6.7 を入れる

gem install nokogiri --verbose -- --with-xml2-dir=`brew --prefix libxml2` --with-xslt-dir=`brew --prefix libxslt`
(ry
/Users/takuya/.rbenv/versions/2.2.3/bin/nokogiri
Successfully installed nokogiri-1.6.7
Parsing documentation for nokogiri-1.6.7
Parsing sources...
100% [179/179]  suppressions/README.txt
Done installing documentation for nokogiri after 3 seconds
1 gem installed

お疲れ様でした!!!!

takuya@~$ rbenv exec nokogiri -v
# Nokogiri (1.6.7)
    ---
    warnings: []
    nokogiri: 1.6.7
    ruby:
      version: 2.2.3
      platform: x86_64-darwin15
      description: ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin15]
      engine: ruby
    libxml:
      binding: extension
      source: packaged
      libxml2_path: "/Users/takuya/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/nokogiri-1.6.7/ports/x86_64-apple-darwin15.0.0/libxml2/2.9.2"
      libxslt_path: "/Users/takuya/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/nokogiri-1.6.7/ports/x86_64-apple-darwin15.0.0/libxslt/1.1.28"
      libxml2_patches:
      - 0001-Revert-Missing-initialization-for-the-catalog-module.patch
      - 0002-Fix-missing-entities-after-CVE-2014-3660-fix.patch
      - 0003-Stop-parsing-on-entities-boundaries-errors.patch
      - 0004-Cleanup-conditional-section-error-handling.patch
      - 0005-CVE-2015-1819-Enforce-the-reader-to-run-in-constant-.patch
      - 0006-Another-variation-of-overflow-in-Conditional-section.patch
      - 0007-Fix-an-error-in-previous-Conditional-section-patch.patch
      - 0008-CVE-2015-8035-Fix-XZ-compression-support-loop.patch
      - 0009-Updated-config.guess.patch
      - 0010-Fix-parsering-short-unclosed-comment-uninitialized-access.patch
      libxslt_patches:
      - 0001-Adding-doc-update-related-to-1.1.28.patch
      - 0002-Fix-a-couple-of-places-where-f-printf-parameters-wer.patch
      - 0003-Initialize-pseudo-random-number-generator-with-curre.patch
      - 0004-EXSLT-function-str-replace-is-broken-as-is.patch
      - 0006-Fix-str-padding-to-work-with-UTF-8-strings.patch
      - 0007-Separate-function-for-predicate-matching-in-patterns.patch
      - 0008-Fix-direct-pattern-matching.patch
      - 0009-Fix-certain-patterns-with-predicates.patch
      - 0010-Fix-handling-of-UTF-8-strings-in-EXSLT-crypto-module.patch
      - 0013-Memory-leak-in-xsltCompileIdKeyPattern-error-path.patch
      - 0014-Fix-for-bug-436589.patch
      - 0015-Fix-mkdir-for-mingw.patch
      - 0016-Fix-for-type-confusion-in-preprocessing-attributes.patch
      - 0017-Updated-config.guess.patch
      compiled: 2.9.2
      loaded: 2.9.2
じゃ、bundle も行ってみよう
echo gem "mechanize" , "=2.7.3" >> Gemfile
bundle install 

出来た

疲れた・・・

少し疲れました。

とりあえず、Nokogiriがインストール出来ないとかいう定番のアレは、brewgcc 関連やgnu コマンド関連がPathに含まれててバッティングしていたということが分かった。

bundle するときは bundle config を使うといい。

いまのnokogiriをインストールしてみた感じだと、殆どの人は bundle install でコケることはないんだろうな。

bundle exec gem  install nokogiri 

または、直接ビルドオプションを指定する。

bundle config  build.nokogiri -- --with-xml2-dir=`brew --prefix libxml2` --with-xslt-dir=`brew --prefix libxslt`
bundle install 

iCloudメールが +ラベル に対応していた件。とメールの+ラベル重要性。

icloud メールが、+ラベルによるメール配信に対応していた。

f:id:takuya_1st:20151129171726p:plain:w400

icloud.com メールを[再発見]してしまった。

ラベルに対応したのはかなり重要です。ラベルは個人情報漏洩への不正アクセス防止にとても強い効果を発揮します。

ラベル利用は重要

ラベルはスパム避けにまるで意味がないとか、「+」ついてるからメアドがバレるというけれど、攻撃者からの保護に有効なセキュリティ対策ですよ。

攻撃者にはログインに使用されてるラベルがわからない

icloud でもラベルが使える、しかも数種類

yourname@icloud.com というメアドがあるとき、iphoneの初期からのユーザーなら

yourname@icloud.com
yourname@me.com
yourname+label@icloud.com
yourname+label@me.com

などと、イキナリたくさんのメアドが使えて便利。エイリアスだけじゃなく、+ラベルを使うことで無尽蔵なメアドが使えるのです。

ラベルはスパムよけではなく、不正アクセス対策

ラベルをスパム対策としてフィルタと組み合わせる例が散見されますが、スパム避けにはなりません。

メアドとパスワードのペアが漏れてもより安心になるのです。

例を見てみる。

+ラベル利用時にメアドが情報漏えいしたときに、なぜ安心か?その例を見てみようと思います。

私が次のメアドで登録しているとき

サイトA takuya+123@example.com
サイトB takuya+ABC@example.com

サイトAとサイトBで使った「メアドは同一」で「ラベル」が相違してます。

サイトAがお漏らしした

ここでサイトAがうっかりお漏らしした。とします。

このとき、攻撃者がサイトAに登録したメアドで攻撃してくるが

仮に攻撃者がラベルを除去して下のメアドで攻撃を試行したしても・・・

攻撃者には、サイトBで私が使ってるラベルが不明

そのため、攻撃者はサイトBのパスワード攻撃が不可能になります。

つまり、攻撃者は「サイトBのログイン用のメアドが見つからない」ことになる

サイトBへきた攻撃者はパスワード総当り攻撃が出来ない。なぜならラベルが見つからない=アカウントがみつからない。

ラベルの意味はスパム避けでなく、攻撃避けです。

ラベルの利用は、「実用的」な総当り対策なのです。ラベルで登録できないでセキュリティ対策を謳うサイトとか滅びればいいんだ。

+ラベルの利用を許可するのは、「自社のユーザー」を総当りや漏洩から保護するためです。

「+ラベル」のメアドを不正形式とせず、利用を許可してほしいです。

◯◯銀行とか、◯◯バンクとか、◯◯カードとかとくに無駄なCookieチェックしてないでこっちヤって下さい。

またメールエイリアスも使える。

よくGmailユーザーがやGmailバンザイの信者がラベルをエイリアスと呼ぶけれど、ソレは違います。

+ラベルとエイリアスは別物です。

icloudではちゃんとエイリアスを登録できる。

1アカウントにつき3つまでメアドを追加することが出来て、スパム避けにも使い分けにも出来ます。

f:id:takuya_1st:20151129172746p:plain:w400

スパム増えてきたら、エイリアスのメアドをまるっと削除すれば、もう届きません。消せばすっきりです。それがエイリアス

iCloudメール、メインでかなり使えそう。

相当量のスパムも弾いてくれるし、Gmailと比較しても遜色ないレベルまで来た。

機能 icloud Gmail
容量 5GB 15GB
追加容量 50GB/$1/月 200GB/$2/月
メールエイリアス  対応  なし
ラベル 対応 対応
IMAP 対応 対応
フィルタルール 対応 対応
SNS 登録 なし  G+強制
iMessage登録 任意 任意
hangouts登録 - 任意

g+の登録強制は、g+失敗により緩くなりましたが、be Evil な彼らがいつ復活させるか・・・

2016-05-16

日本語が不明確なところを書き直した。

7SpotのWifiの自動ログインを書き直した。

7Spot のログイン仕様が変わってた

f:id:takuya_1st:20151128224536p:plain:w200 久しぶりに、ヨーカードーに行ったのでネットをしようとしたら、自動ログインが動かないので、仕様を再調査してきた。

github 更新した

github.com

あとで役に立つように

備忘録的に残しておこうと思う。

7Spot で調べて分かったこと

7Spotでアレコレ調べて分かったこと

  • TwitterなどSNSはログイン無しで見られる。
  • Https を素通してくれる(googleなど)
  • DNSルックアップは通る。
  • WiSPrには非対応
  • CaptiveNetworkを無視してくる
  • JavaScript によるCookie/Location書換でログインチェックしてる
  • User-Agent によるフィルタリングが掛かってる(curlでアクセス出来ない)

アプリ通信を阻害しないように「工夫」*1をしてあるようだ。

とくに、問題なのがiOS/OSXでの利用、AppleiOSで公衆無線LANの検出に使ってるCaptiveNetwork関連で使う http://www.apple.com/library/test/success.html へのアクセスを「素通し」してしまうので、ネットワーク検出がうまくいかない。どうなってんだろう。設計者はマトモにググったのか。

更に問題なのが、JS+Cookieによるクライアントチェック。これが特に厄介。

標準ブラウザ以外のログインを切断するようなので、スクリプトやアプリに依るログインが上手くいかない。

こういうクソ公衆無線LANが「外国人向け」とかになると余計に混乱が生じるだけな気がする。

7Spotやファミマのスーパ系のWifiは、なんか変な仕様が多い。連中は社内ルールをインターネットのルールと勘違いしているフシがある。古くは特定のURLにアクセスできない無線LANを作ったしね。

ログインのチェックの仕組み

ログインの流れは次のようになってた。

  1. www.yahoo.co.jp などに http でアクセスすると強制リダイレクトがかかる
  2. リダイレクト先は http://redir.7spot.jp/redir/ 
  3. リダイレクト先で、さらにログインへのリダイレクトがかかる。http://webapp-ap.7spot.jp/check.html?tmst=1448711533 ( ←int 秒)
  4. 該当ページで cookiecheck=true にするJSが動作し、location.href="/?tmst=1448711" (←int秒÷1000 )
  5. トップ画面が表示される。
  6. ログインページヘ

一見すると上手く動くようになってるけど、通信会社がWiSPr対応でXML提示してくれるのと大きく違ってる。JSチェックが走るのでログイン機構でWebkitを起動する必要があって、ちょっとマナーが悪いかなって思う。

JSチェックの部分

露骨に true 入れてました。

tmp = "cookiecheck="+escape('true')+"; expires=" + expires;

文字列にescape つけるってセンスは「セキュリティ対策」だと思いますが、意味なしなescapeをやらなくちゃいけないのは可哀想ですね。

function getCookie( key, tmp1, tmp2, xx1, xx2, xx3 ){
  tmp1 = " " + document.cookie + ";";
  xx1 = xx2 = 0;
  len = tmp1.length;
  while (xx1 < len){
    xx2 = tmp1.indexOf(";", xx1);
    tmp2 = tmp1.substring(xx1 + 1, xx2);
    xx3 = tmp2.indexOf("=");
    if (tmp2.substring(0, xx3) == key){
      return(unescape(tmp2.substring(xx3 + 1, xx2 - xx1 - 1)));
    }
    xx1 = xx2 + 1;
  }
  return("");
}

var nowtime = new Date().getTime();
var clear_time = new Date(nowtime + 5000);
var expires = clear_time.toGMTString();

tmp = "cookiecheck="+escape('true')+"; expires=" + expires;
document.cookie = tmp;
cookiecheck = getCookie("cookiecheck");
android4 = (navigator.appVersion.indexOf('Android 4') > -1);
if ( (!android4 && cookiecheck == "true") || (android4 && navigator.cookieEnabled)){
  location.href = "/?tmst=" + Math.round((new Date()).getTime() / 1000);
}
tmp = "cookiecheck="+escape('')+"; ";
document.cookie = tmp;

*1:といっても場当たり的でとてもマトモな対応とはいえない。

Array(10) がundefined になる問題。

js で要素が空っぽの配列10個作ってループ変数代わりに使ってみよう

forループとかダルいよねと思って。10個ループ変数してみたら。。。

Array(10).forEach( (e)=>{ console.log(e) } )
//=> undefined

あれ?なんで?

map で初期化したら?

Array(10).map( (x)=>{return 1} )
[undefined × 10]

あれ?出来ない。

配列の長さは10だよね?

うん、配列長は指定通りなんですね、

Array(10).length
//=> 10 

あれ?

undefined の扱いの問題かな?

Array(10)[100]
//=> undefined

Array(10) はこういうこと

Arrayの第一引数に数値を指定すると、次のような動作になってるみたい。

a = new Array()
a.length = 10
a.map( (x) =>{ return 1 });
//=>[undefined × 10]

解決策

Array(10).fill(1)

あまり美しくないけど、どこでも使えるといえばこうなる。

Array.prototype とか追いかけて、深入りすると沼に嵌りそう。

関連資料

JavaScript で 100.times( alert ) するrangeっぽいものも - それマグで!

参考資料

JavaScript - new Array(len)と[undefined, ...]の違い - Qiita

curl で http2 にアクセスする

HTTPヘッダを見たらいつものと違うヘッダがあった

http2 だ!

そういえば、そろそろHTTP2を使うサイトも増えてきて、まぁ規格も知ってたほうがイイなと思いました。

curl でhttp2にアクセスする

brew そのままではインスト出来ないのと、osx に付属のcurlでは出来ないので、コンパイルする

brew reinstall curl --  --with-nghttp2
==> Reinstalling curl with --with-openssl, --with-nghttp2
==> Installing dependencies for curl: libev, jansson, spdylay, nghttp2
(略
######################################################################## 100.0%
==> Pouring nghttp2-1.4.0.el_capitan.bottle.tar.gz
🍺  /usr/local/Cellar/nghttp2/1.4.0: 201 files, 11M
(略
==> Summary
🍺  /usr/local/Cellar/curl/7.45.0: 355 files, 3.2M, built in 3.4 minutes

リンクして使えるようにしておく

brew link curl --force
alias rehash="hash -r "
rehash

リクエストを送信してみる

takuya@~/Desktop$ curl --http2 -I https://http2bin.org/get
HTTP/2.0 200
server:h2o/1.5.0
date:Fri, 27 Nov 2015 15:10:32 GMT
content-type:application/json
access-control-allow-origin:*
access-control-allow-credentials:true
x-clacks-overhead:GNU Terry Pratchett
content-length:252

他にも試してみよう

takuya@~/Desktop$ curl --http2 -I https://twitter.com/
HTTP/2.0 200
cache-control:no-cache, no-store, must-revalidate, pre-check=0, post-check=0
content-length:252060
content-type:text/html;charset=utf-8
date:Fri, 27 Nov 2015 15:09:46 GMT
expires:Tue, 31 Mar 1981 05:00:00 GMT
last-modified:Fri, 27 Nov 2015 15:09:46 GMT
pragma:no-cache
server:tsa_a
(略
x-xss-protection:1; mode=block

HTTP/2 で通信した時のヘッダ

HTTP/1.1 で表示されるヘッダと若干差異がある表示になる。

f:id:takuya_1st:20151128001648p:plain

Chromeの開発ツールでHTTP2がパット見でわからないから、時々あれれってなるのです。

リクエストヘッダ・レスポンスヘッダがいつもとほんの少しだけ異なるので、時々困る。

参考資料

C 言語: curl で HTTP/2 GET リクエストを送信する - Sarabande.jp

指定の要素(ノード)の直後に、HTML要素を追加する。

ある指定要素の直後に要素を追加したい。

こういうインプットやHTMLがあった時に、直後に要素を追加したい

<input type="text" placeholder="なにか書いて" /><br>
<a href="#">ここはリンク</a>
<input type="text" placeholder="なにか書いて" /><br>
<span>ここは追加された</span>
<a href="#">ここはリンク</a>

動作サンプル


ここはリンク

アプローチは2つ

  1. interface Elementを使う。
  2. interface Nodeを使う。

1. Element.insertXXを使う

var e = document.evalue(略)
e.insertAdjacentHTML("afterend","<span>コレはテスト</span>")

こちらはXML的にノードの直前に要素を追加する。

参考資料 →

2.Node.insertBeforeを使う。

insertBeforeで、自分の兄弟ノードで後ろにあるノードの直前に追加すれば、自分の直後に追加したことになる。

var a = document.createElement("a")
a.textContent = "リンク"
a.href = "http://www.yahoo.co.jp"

var e = document.querySelector("#id");
e.parentNode.insertBefore(a, e.nextSibling);

あるノードの直後

ノードの直後に挿入するには insertBeforeを少しとトリッキーに使うことが出来るってのがオモシロイよね。

ノードの最終で後ろにノードがなくてもなんとか動くっぽい。

自分の要素の真後ろを示し、そこに要素を追記するには、HTML・XML的な解決方法があるんですね。

append とか after とかで使えるのはこのおかげですね。

参考資料

iTunes 12 以降で「リピート再生」をする

iTunes 12 で消えた リピート再生を取り戻す。

方法1dock アイコンを右クリックする。

f:id:takuya_1st:20151127184420p:plain:w300

方法2シャッフルボタンを右クリック

f:id:takuya_1st:20151127184606p:plain:w300

オススメDockアイコンの右クリック

Dockアイコンを右クリックするほうが確実でした。

なぜ、Dockアイコンを使うのか?

iTunesのメニューよりDockがオススメな理由。

iPhone とitunes の同期

同期の表示が優先されて、シャッフルのボタンが消えるんです。最悪。

なんとかならないですかねぇ

せめて「ミニプレーヤー」にアレばいいんですけどね。

Windows版はどうなんだろうね。