sudo.cmd / su.cmd2015年10月18日 21時57分13秒

コマンドラインから管理者に昇格したい

たとえば、mklinkでシンボリックリンクを作りたい場合、いちいちcmd.exeを右クリックから「管理者として実行」するのは非常に面倒くさく、いま開いてるプロンプトから権限昇格できると便利だよね、と思って調べると、まぁいろいろな方々がすでにやってたりするわけです。

要は、WSHでShell.Application(エクスプローラを外部から操作するためのActiveXインターフェイス)で実行ファイルに対して「管理者として実行」で起動させる、という仕組みを使う、と。

んで、久々になんちゃってコマンド

んで、まぁ、suとかsudoとかを作ってみようかと。いや、先ほどのGoogle検索の結果に出てくるあちこちの記事でもすでに実現されてるんだけど、なんとなく自分でもやってみるか、という程度で。

suとsudoを別々に実装してもいいんだけど、cmd.exeを「管理者として実行」できればsu、任意のコマンドならsudoになるので、同じスクリプトを別のバッチファイルから呼び出す方式で実装することに。

ダウンロード

こちらからダウンロードをどうぞ。

sudo / suコマンド一式

zipには

  • scripts/sudo.js
  • su.cmd
  • sudo.cmd
の3つのファイルが格納されてるので、この構造を維持したままパスが通ったところに設置するとコマンドラインからいつでも使えるようになります。

ソース - scripts/sudo.js

スクリプトのソースはこんな感じ。

キモは最後のほうにある「new ActiveXObject("Shell.Application").ShellExecute()」のところで、ここで第4引数に"runas"を与えると、エクスプローラのコンテキストメニューから「管理者として実行」を行ったことになる、という感じです。詳しくは以下のリンク先を参照。

Shell.ShellExecute method (Windows)

ソース - sudo.cmd

sudoのバッチはこんな内容です。

バッチに渡された引数をそのままsudo.jsに丸投げしてるだけの簡単なつくりです。

ソース - su.cmd

suのバッチはこんな感じ。

sudo.cmdでは引数をそのまま丸投げしてたところを、明示的に「cmd.exe /k」しています。

あと、スクリプトを実行すると新しいプロンプトが開くので、なにも考えずに使ってるとコマンドプロンプトのウィンドウだらけになってウザいので最後にexitしてますが、これはお好みで削除してもよいでしょう。

あ、それからsudo.js側で小技を入れているので、suした場合は元のディレクトリを引き継ぎます。

使い道とか

suしてmklinkしてシンボリックリンクをバンバン作成するとか、hostsいじるのにsudo notepadするとか、まぁそんな感じでしょうか。

即吊ブックマークレット改善版2015年05月26日 20時48分50秒

iOSでうまくいかなかったので…

前のエントリに載せたブックマークレットはソースが長すぎてiCloudで同期できなくてiOSでの動作確認もままならなかったので、意地になって改善しました。なんとか使い物になるかなぁ、と。

改善版ブックマークレット

即吊ダイアログ[改]

デスクトップブラウザなら↑のリンクをブックマークツールバーにドラッグドロップしてください。リンクをクリックするとpromptでjavascript:なURLを表示するので、スマートフォンやらタブレットの場合はこれをコピーした上で適当なページをブックマーク、編集で貼り付けてください。

ソース

こんな感じです。ブックマークレットにするにあたってはこのソースをpackerで圧縮して<をエスケープしています。

即吊ブックマークレット2015年05月21日 01時35分49秒

またの名を「ソク吊嵐」

即吊☆キャプチャー|WEBキャプチャーをTwitterに投稿しちゃうアプリケーション ってなサイトがあってですな。サイトの画面をキャプチャしてさくっとTwitterに投げ込むわけですよ。いたく便利で。

投稿するキャプチャー画像にコメント、ハッシュタグを入れる。特定のツイートにリプライするなど出来ます。

勢いです。勢い。

オプションもいろいろあって、ブックマークレットも用意されてるんだけども、prompt()連発のウィザード形式で、少々戸惑っちゃうんですわ。すぐに慣れるんだけども。

そこでなんとなく勢いでダイアログベースのブックマークレットを作ってみたわけなんですわ。ええ。

即吊ダイアログ

※:2015.5.25 ダイアログにスタイル適用したり、実験中パラメータ「callback」に対応したりで更新しました。

※:2015.5.26 ブックマークレットのURIが長すぎるせいか、iCloudでの同期がうまくいきません&iOSだとうまく動かないかも。

iCloudで同期できるサイズまで小さくしてiOS 8で動作するバージョンを作成しました。

吊りたいページでブックマークレットを起動するとこんなダイアログが出ます。

Firefox 38とChrome 42(どちらもWindows)でしか試してないし、オプションも追加コメントとお仕事場モードくらいしか試してないんだけども、まぁ一応使えるのではないかと。サイトによってはレイアウト崩れたりいろいろ問題ありそうだけども。

中身はこんな感じ(雑です)。

(function(act){
  var w=window,
    d=w.document,
    u=w.location.href,
    ex=function(){
      var args=Array.prototype.slice.call(arguments),ds=(args.shift()||{}),sr=(args.shift()||{});
      for(var p in sr){
        ds[p]=sr[p]
      }
      return args.length?ex.apply(null,[ds].concat(args)):ds;
    },
    dc=function(n){
      return d.createElement(n);
    },
    esc=function(s) {
      return s
        .replace(/&/g,'&amp;')
        .replace(/</g,'&lt;')
        .replace(/>/g,'&gt;')
        .replace(/'/g,'&apos;')
        .replace(/"/g,'&quot;');
    },
    term=function(e){
      setTimeout(function(){
        e.parentNode.removeChild(e);
      },100)
    },
    st={
      d:{
        fontSize:'14px',
        lineHeight:1.4
      },
      fm:{
        textAlign:'left',
        backgroundColor:'#fff',
        color:'#000',
        position:'absolute',
        width:'600px',
        padding:'10px',
        top:0,
        right:0,
        zIndex:9999,
        border:'solid 1px #999'
      },
      ttl:{
        textAlign:'center',
        fontWeight:'bold',
        fontSize:'20px',
        margin:'-5px -5px 5px -5px',
        backgroundColor:'#f9c',
        color:'#c71585'
      },
      txt:{
        width:'360px',
        border:'solid 1px #999'
      },
      lbl:{
        width:'195px',
        marginRight:'5px',
        display:'inline-block',
        textAlign:'right'
      },
      btn:{
        width:'150px',
        margin:'5px 5px 0 5px',
        padding:'4px 10px',
        border:'solid 1px #666',
        backgroundColor:'#eee'
      }
    },
    trimFields=function(fm){
      for(var ls=fm.getElementsByTagName('input'),i=0,l=ls.length;i<l;i++){
        if((ls[i].type=='text'&&!ls[i].value.length)||(ls[i].type=='checkbox'&&!ls[i].checked)){
          ls[i].disabled=true;
        }
      }
      setTimeout(function(){fm.submit();term(fm)},0)
    };
  d.body.appendChild((function(){
    var fm=ex(dc('form'), {
        method:'get',
        action:act,
        target:'_blank',
        onsubmit:function(){trimFields(fm)}
      }),
      title=ex(dc('div'),{innerHTML:esc('ソク吊嵐ダイアログ')}),
      btn=null;
    ex(fm.style,st.d,st.fm);
      ex(title.style,st.d,st.ttl);
    fm.appendChild(title);
    var items=[
      {lbl:'吊るURL',ttl:'即吊したいURLを入れてください(必須です。)',ele:ex(dc('input'),{type:'text',name:'url',value:u,readonly:'readonly'})},
      {lbl:'ハッシュタグ',ttl:'ハッシュタグを付けます。全角半角空白、カンマで多重に付けることができます。',ele:ex(dc('input'),{type:'text',name:'hashtag'})},
      {lbl:'サイト全体キャプチャを試行',ttl:'allとリクエストすると出来る限りWEBサイト全体をキャプチャーしようと頑張ります。',ele:ex(dc('input'),{type:'checkbox',name:'images',value:'all'})},
      {lbl:'追加コメント',ttl:'コメントを付けることが出来ます。',ele:ex(dc('input'),{type:'text',name:'text'})},
      (function(){
        if(!/^https:\/\/twitter\.com/.test(u))return {};
        return {lbl:'このツイートにリプライ',ttl:'1とリクエストすると吊るサイトがツイートならば、そのツイートに.@でリプライを付けて投稿します。',ele:ex(dc('input'),{type:'checkbox',name:'reply',value:'1'})};
      })(),
      {lbl:'返信元ツイートURL',ttl:'吊るサイトを狙ったツイートに.@でリプライを付けて投稿します。(ツイートのURL入れてね。)',ele:ex(dc('input'),{type:'text',name:'status'})},
      {lbl:'職場モード',ttl:'1とリクエストするとターミナル風CSSモードで投稿します。お仕事場でも恥ずかしくありません。かも',ele:ex(dc('input'),{type:'checkbox',name:'terminal',value:'1'})},
      {lbl:'自分にナルト',ttl:'1とリクエストするとピリオド無しの@でこっそり魚拓したのを自分に向けてメンションします。',ele:ex(dc('input'),{type:'checkbox',name:'mention',value:'1'})},
      {lbl:'戻り先',ttl:'吊り終わったら飛びたいURIにを入れてください。ページ出力前にページ遷移しちゃうのでちょっと動作が早いですが><',ele:ex(dc('input'),{type:'text',name:'callback'})}
    ];
    for(var i=0,l=items.length,item=items[i],lbl;i<l;i++,item=items[i]){
      var ln=dc('div');ex(ln.style,{margin:'3px 0'});
      if('lbl' in item){
        lbl=ex(dc('span'),{innerHTML:esc(item.lbl)});
        ex(lbl.style,st.d,st.lbl);
        ln.appendChild(lbl);
      }
      if(('ttl' in item)&&lbl){
        ex(lbl,{'title':item.ttl});
      }
      if('ele' in item){
        if(item.ele.type=='text'){
          ex(item.ele.style,st.d,st.txt);
        }
        ln.appendChild(item.ele);
      }
      fm.appendChild(ln);
    }
    ln=dc('div');ex(ln.style,{marginTop:'10px',borderTop:'solid 1px #ccc'});
    ex(ln.style,{marginTop:'10px',textAlign:'center'});
    fm.appendChild(ln);
    btn=ex(dc('button'),{type:'submit',innerHTML:esc('吊る!')});ex(btn.style,st.d,st.btn);
    ln.appendChild(btn);
    btn=ex(dc('button'),{type:'button',innerHTML:esc('やめる'),onclick:function(){term(fm)}});ex(btn.style,st.d,st.btn);
    ln.appendChild(btn);
    return fm;
  })())
})('http://drinker.slfeed.net/archive/')


tumblrのキーボードショートカットが便利になっとる。2012年10月17日 21時41分27秒

すんごい久しぶりにtumblrのことなど

それ以前にブログ更新するのがすんごい久しぶり、ってのは置いておいて。

いまだにメインブラウザがFirefoxなもので、tumblrのダッシュボード向けに、執念深く古いグリモン+Minibuffer+LDRize+Autopagerizeという環境で回しているんだけども、最近ふと気が付いたらダッシュボードでjした時に「カクッ」と動くようになってることに気付いた。

いつのまにか標準キーバインドが復活しとる

結構前からダッシュボードには、標準でキーバインドが組み込まれてて、使いづらいので これをキャンセルするグリモンスクリプトを組み込んでたんだけど、どうやらこれが効かなくなったらしい。

何日か前にこれに気付いて、なんとかキャンセルできるようにしたんだが、今日になったらまたコードが効かなくなってた。矢継ぎ早なアップデートみたい。

なんとか解除してみる

仕方ないのでまたJSのデバッグコンソールをごにょごにょいじって調べたところ、いつの間にやらキーバインドを処理するための Tumblr.KeyCommands なるオブジェクトが加わってて、これが suspended プロパティやら、suspend() メソッドなんかが実装されてることが分かったので、ためしにグリモンで

unsafeWindow.Tumblr.KeyCommands.suspend();
なんてやってみたら見事にキャンセルできた。

ところで、標準キーバインドってなにができるん?

KeyCommands 周りを調べてる過程で気づいたんだけど、「like」とか「reblog」、「notes」、「play」なんて興味深いメソッドが実装されてて興味が出たので、どんなキー操作ができるのかを調べてみた。tumblrのほうにエントリ挙げたけどあんまり見やすくないので、以下にもう一度まとめておく。

j(ジェイ)次のpostへ移動
k(ケー)前のpostへ移動
l(エル)like / like解除
n(エヌ)notesを開く / 閉じる
r(アール)reblog(別ウィンドウが開く)
R(Shift+アール)即時reblog
E(Shift+イー)キュー(投稿予約)へreblog
スペース動画再生?(未確認)
カーソルキー左前のページへ
カーソルキー右次のページへ

謎の処理もあるんだけども

この他に修飾キー(Windowsの場合はWindowsキー、Macの場合はoptionキーと思われる)を押しながら以下のkeyCodeでTumblr.blog_switcher()という処理を呼び出してるみたい。

Windows 9(Tab)、27(Esc)、192(???)
Mac 9、27、192、37(カーソル左)、39(カーソル右)

なんだけど、これが何をするのかよくわからんし、自分の環境ではなにも起きないのでとりあえず無視

標準キーバインドのほうが便利くさいので方針転換

先のキーバインドの機能、大体はこれまでのグリモン環境でもできてたことなんだけども、即時reblogは自分の環境ではできなかったので非常に魅力的。

ということで、www.tumblr.com ではMinibufferもLDRizeも外すことにして、標準キーバインドを使うようにするよう方針転換。ついでといってはなんだが、標準同士の組み合わせのほうが安定してるのは目に見えてるのでAutopagerizeも外して、settings/dashboard で「Enable endless scrolling」を有効に。

標準のendless scrollingを有効にしたのでiPadで見る場合なんかはかえって便利になったりといううれしい誤算もあり、結構うれしかったり。

でもちょっと手を入れないと

ただ、標準機能のままだとpostの移動が「カクッ」となっててこれはちょっと気持ち悪いのでなんとかならんかと調べたところ、 Tumblr.KeyCommands が「animate_scroll」というプロパティを持っていた。さっそくこれをtrueに設定したら案の定スムーズスクロールするようになって快適。

また、animate_scroll = true の状態でも個人的にはちょっと速度が速いように感じられたので、一緒に見つけてた「scroll_speed」プロパティをデフォルトの100から150にしたら結構いい感じ。

これらの設定(どころか、キーバインドの有効/無効の切り替えすら)はtumblrの設定からいじれないので、ひとまず

unsafeWindow.Tumblr.KeyCommands.animate_scroll = true;
unsafeWindow.Tumblr.KeyCommands.scroll_speed = 150;
みたいなグリモンスクリプトを作って対応。

オマケ:ブックマークレットも作ってみたよ

いちいち起動しなきゃなんないのは面倒だけど、おんなじようなことを行うブックマークレットも作ってみた。

スムーズスクロールを有効に

中身はこんな感じも。

(function() {
    if(Tumblr && Tumblr.KeyCommands) {
        with(Tumblr.KeyCommands) {
            animate_scroll = true;
            scroll_speed = 150
        }
    }
})()

dataスキームの画像をブロックしてみる - Chrome編2012年03月22日 03時52分25秒

前のエントリの続きで、今度はGoogle Chrome行ってみよう。といっても、それほど書くことあるわけじゃないので比較的アッサリ気味の内容で。

他にもいろいろあるんだけど、Firefoxでソースをざっくり眺めて馴染みがでたので、ターゲットはAdblock Plus。現在タイトルに「(Beta)」がついてる1.2版です。

こちらもFirefox版と同じく、単純に「data:image/*」みたいな登録をしてもフィルタリングされないので、またまたデバッグコードを仕掛けて調べてみることに。

extensionのインストールされたディレクトリをざっと眺めてみたらmanifest.jsonてのがあったのでそちらを見てみる。

manifest.jsonの内容から推測するに、メインで使用されるのはどうやら「contentScript2.js」らしいのでそちらを見てみたところ、beforeloadHandlerという関数からshouldBlock()にURLを渡して判断してるっぽいのでその辺りにデバッグコードを仕掛けて調べたら、dataスキームURIを相対URLとみなして、現在のドキュメントURLからの絶対URLに変換してた。こりゃ引っかからんわ。

コードにパッチ当てちゃってもいいんだけど、モノは試しで「http://*/*data:image/*」なんてフィルタ登録してみたらあっさりうまくいっちゃった。まぁURLの途中に「:」を挟んだパターンがあったら誤爆する可能性もあるんだけど、とりあえずこれでいいか。

ただ、URLフィルタにマッチした場合でもHTTPでの通信そのものは発生しちゃうみたいで、それだったら「##img[src*="data:image"]」のほうが誤爆の恐れもないし安全かも。

あ、あとcssでbackground-imageに指定してる場合なんかはブロックされないっぽいです。