本当に Windows10 のコマンドプロンプトはエスケープシーケンスをサポートしたのか?

したっぽい。実験してみよう

package main

import (
    "fmt"
    "os"
    "syscall"
    "unsafe"
)

var kernel32 = syscall.NewLazyDLL("kernel32")

const STD_INPUT_HANDLE = uintptr(1) + ^uintptr(10)
const STD_OUTPUT_HANDLE = uintptr(1) + ^uintptr(11)
const STD_ERROR_HANDLE = uintptr(1) + ^uintptr(12)
const ENABLE_VIRTUAL_TERMINAL_PROCESSING uintptr = 0x0004

var procGetStdHandle = kernel32.NewProc("GetStdHandle")
var procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
var procSetConsoleMode = kernel32.NewProc("SetConsoleMode")

func Main() error {
    var mode uintptr
    console, _, _ := procGetStdHandle.Call(STD_OUTPUT_HANDLE)

    rc, _, err := procGetConsoleMode.Call(console, uintptr(unsafe.Pointer(&mode)))
    if rc == 0 {
        return err
    }
    defer procSetConsoleMode.Call(console, mode)

    rc, _, err = procSetConsoleMode.Call(console, mode|ENABLE_VIRTUAL_TERMINAL_PROCESSING)
    if rc == 0 {
        return err
    }
    println("\x1B[32;1mAHAHA\x1B[37;1m")
    return nil
}

func main() {
    if err := Main(); err != nil {
        fmt.Fprintln(os.Stderr, err.Error())
        os.Exit(1)
    } else {
        os.Exit(0)
    }
}

image.png

SetConsoleMode で、ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x0004) というビットを立てなくてはいけないようです。

くわしい仕様はこちら:

Go でやる分には go-colorable があるので、あんまりすぐ使う必要性はありませんが、いつが Windows 7/8 がサポート切れになった時、使う日が来るかもしれません。

NYAGOSの小TIPS集

NYAGOS 4.2.0 以降が対象です。

ファイルはゴミ箱へ

trash ファイル名

でファイルがいきなり削除ではなく、ゴミ箱に入ります。実体は Lua スクリプトで、COM経由でゴミ箱へファイルを移動しています。(nyagos.d/trash.lua

UTF8 変換に type 文

内蔵 type 文は下記の拡張されています。

  • 行単位で UTF8 変換
    • テキストが UTF8 として妥当であれば UTF8 と判断、不適当なら現在のコードページの文字種類と判断(つまり、混在データでも大丈夫)
  • 標準入力からも読み込み可能

これを利用すると

git show . | type | gvim - &

などということが可能になります。

  • Windows で git やってると、git の日本語メッセージと、ソースのテキストで文字コードが違っていて文字化けが回避できないことがあるが、内蔵 type 文は行単位で判別しているので、ほぼ読める内容になる
  • nkf (Network Kanji Filter)の文字コード判別ロジックに比べると、う「エラーだったら UTF8 と違う(ANSI=現在のコードページ)」とい実に雑なUTF8判定だが、案外十分
  • NYAOS-3000 では内蔵コマンドをパイプラインの最初以外のコマンドに使えなかった。NYAGOS ではどこでも内蔵コマンド(Lua含む)が使える

深いディレクトリ移動はショートカットで

いちいち cd ~/go/src/github.com/zetamatta/nyagos なんて長いパスを打ってられるか!

そこでショートカットですよ。

lnk ~\go\src\github.com\zetamatta\nyagos .

と一度ショートカットを作成しておけば、以後は

cd nyagos.lnk

だけでいいです。

git push 漏れはないかな?

%APPDATA%\NYAOS_ORG\nyagos.history に、タイプしたコマンド、日時、カレントディレクトリ全部が TSV 形式で記録されるようになりました。これを検索する PowerShell を書いてみましょう。

$done = @{}
Join-Path $env:appdata 'NYAOS_ORG\nyagos.history' |
    %{ Get-Content $_ -Encoding utf8 } |
    ?{ $_ -match '^[g]it commit' } |
    %{
        $private:tmp=($_ -split "`t")
        # $private:from = (Get-Date).AddDays(-3).ToString("yyyy-MM-dd")
        $private:dir=$tmp[1] 
        if( (Test-Path $dir) -and (-not $done.ContainsKey($dir)) ){
            Write-Host $dir
            pushd $dir
            git status | ?{ $_ -match "^Your branch is" } | %{ "   "+$_ }
            popd
            Write-Host
            $done[ $dir ] = 1
        }
    }
$ todaygit.ps1
C:\Users\hymko\go\src\github.com\zetamatta\nyagos
   Your branch is up-to-date with 'origin/second'.

C:\Users\hymko\go\src\github.com\zetamatta\nyagos.wiki
   Your branch is up-to-date with 'origin/master'.

C:\Users\hymko\go\src\github.com\zetamatta\experimental\zero
   Your branch is up-to-date with 'origin/master'.

C:\Users\hymko\Share\bin
   Your branch is up-to-date with 'origin/master'.
  • 普通、PowerShell スクリプトを実行する時は「PowerShell -ExecutionPolicy RemoteSigned -file などとセキュリティーオプションをつけなくてはいけません。同オプションをデフォルト化することもできますが、OSユーザ全体にその設定が及んでしまいます。nyagos の場合は、デフォルトで .ps1 拡張子の時に「suffix ps1="powershell -ExecutionPolicy RemoteSigned -file"」というインタプリタ名を追加する設定になっており、nyagos から呼び出す時のみスクリプト実行を許可されることが可能になっています。
  • 最初は「その日に行った git commit」のみを対象としていたのですが、月曜朝に土日の commit 漏れをチェックするといった時に困ること、どのみち history には 1000件程度しか記録していないことから、別に日付を限定する必要はないと判断しました。

外部の sudo を使いたい

sudo コマンドは Windows では一般に管理者権限でコマンドを実行する実装が多い。NYAGOS でも内蔵コマンドとして実装していたが、新しいコンソールを立て上げてしまうので、コンソールが変わらない、他の sudo for Windows を使いたい。

そんな時は

alias "sudo=sudo.exe"

と EXE 版を呼び出すよう、 ~\_nyagosエイリアスを書きましょう。

  • 不評な内蔵版 sudo は次のバージョンでは削除される予定。suclone は存続

以上

Visual Studio を NYAGOS から使う時

普通は Visual Studio のパスを通すため、下記のように .nyagos 上で Visual Studio コマンドプロンプトのバッチを source する。

nyagos.exec{'source',[[C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat]],'x86'}

これやると NYAGOS の起動が遅くなるし、Visual Studio を複数バージョン入れてたりするとパスを自動で変更するのとか結構たいへんになる。

でも、Visual Studio を開くだけだったら open *.sln で済む。

ビルドはさすがに Visual Studio にパスを通さないとダメかなぁと思ってたが、 MSBuild を実行できれば、なんとかなるみたいだ。 理論上は MSBuild へのパスも通さないといかんわけだが、バッチ(make.cmd)で次のように検索して、フルパスで起動させたらいけた。

for /D %%I in (C:\Windows\Microsoft.NET\Framework\v*) do set "MSBUILD=%%I\MSBuild.exe"
"%MSBUILD%" xxxxx.sln /p:Configuration=%1

オニキスめざしてがんばりましょう

追記

MSBuild でビルドすると、なぜか、作成される DLL の拡張子が .dll.dll になった。 原因は分からんが、これは困るので

:msbuild
    for /D %%I in (C:\Windows\Microsoft.NET\Framework\v*) do set "MSBUILD=%%I\MSBuild.exe"
    echo MSBUILD=%MSBUILD%
    "%MSBUILD%" %SLN% /p:Configuration=%1
    for /R %%I in (*.dll.dll) do move "%%I" "%%~dpnI"
    @exit /b

というコード書いた。

OJTばかりやって

OJTばかりやって、座学や書籍で理論をちゃんとやらんから、妙な社内標準・社内フレームワーク・社内ローカルルールが出来て、他所で通用しない社員ばかりになるんだな

https://twitter.com/zetamatta/status/881672613889679360

四国の僧侶の先生が言ってた「瞑想ばかりやって、仏法の勉強をしないと、自分の超体験だけが標準になって、妙な新興宗教を始める」のと同じだと思う

https://twitter.com/zetamatta/status/881674952562270213

readline ライブラリ

nyagos 以外からでも簡単に使えるよう、いろいろがんばった

package main

import (
    "context"
    "fmt"
    "github.com/zetamatta/nyagos/readline"
)

func main() {
    editor := readline.Editor{
        Default: "InitialValue",
        Cursor:  3,
    }
    text, err := editor.ReadLine(context.Background())

    if err != nil {
        fmt.Printf("ERR=%s\n", err.Error())
    } else {
        fmt.Printf("TEXT=%s\n", text)
    }
}

よかったら使ってね

俺は荒木飛呂彦先生は…

ジョジョ第三部の途中で亡くなっていて、今描いているのは二代目ではないかと思っている。

  • 画風が変わっている
  • 登場人物が主人公をジョジョと全く呼ばなくなる
  • このあたりから各話を適当に入れ替えても成り立つようなストーリー構成へ
  • 先生自身が若返っている

https://twitter.com/zetamatta/status/877691788986679296

ちょっと前に「有名漫画家が事故で死んで、編集者とチーフアシスタントがその死を隠蔽しつつ、 続きを描いて、その巨額の財産を奪おうとする」という漫画があったなぁ。名前忘れたが

https://twitter.com/zetamatta/status/877692165232533504

Windows における su(sudo) 事情考察

su (sudo)とは

suというと、通常は UNIX(Linux)で、ログアウトせずにユーザを切り替えるコマンドを指し、主に root (管理ユーザ)に切り替える用途に使われることが多い。そして、sudo は1コマンドだけユーザを切り替えて実行するコマンドだ。

最近、Windows でも su(sudo)的なコマンドが現れたが、これは常に管理ユーザで使われがちの Windows においては、ユーザを切り替えるというよりも、管理者権限を獲得するためのコマンドという位置づけとなっていることが多い。

本文書では、管理者権限を得てコマンドを実行する操作について、手法を紹介してみたい。

管理者権限があるかどうかのチェック

C++

advapi32.dll の OpenProcessTokenGetTokenInformation という API を組み合わせてチェックするという方法がある。詳しく解説しているページはこち

自分でも Go 言語で実装してみた。(nyagos'Lua からはnyagos.elevated()で利用できる)

バッチファイル

net session >nul 2>&1」は管理者権限でないと ERRORLEVEL=2 で失敗するということを利用して判別できる。このテクニックは「Office 365のデスクトップアプリケーションをバッチファイルで修復」で用いられていたが、なかなか手軽でよいと思われる。

管理者権限を得るには

C++

WindowsAPI で管理者権限を得るのは ShellExecute になる。

Windows ではプロセスを起動する有名どころの API としては CreateProcess と ShellExecute の2種類がある。CreateProcess は「普通」にプロセスを起動する API で、パラメータがやたら多くて難しい(← いいすぎ)他は特筆すべきことはない。たいていの言語の子プロセス起動ライブラリはこれを使用している。一方、ShellExecute はデスクトップからアイコンをクリックしたのと同じような形でコマンドを実行するような API である。

ShellExecute のパラメータは CreateProcess よりむしろ簡単で、次のような形となる。

https://msdn.microsoft.com/ja-jp/library/cc422072.aspx より引用:

HINSTANCE ShellExecute(
    HWND hwnd,              // 親ウィンドウのハンドル
    LPCTSTR lpVerb,         // 操作
    LPCTSTR lpFile,         // 操作対象のファイル
    LPCTSTR lpParameters,   // 操作のパラメータ
    LPCTSTR lpDirectory,    // 既定のディレクトリ
    INT nShowCmd            // 表示状態
);

lpVerb に入れる文字列で L"open" を与えると、普通の起動になるが、それを L"runas" と変えると、管理者として実行となる。これを利用した su/sudo はかなり多く、NYAGOS に内蔵している su / sudo もこれである

.NET Framework

Dim pinfo1 As New System.Diagnostics.ProcessStartInfo()
pinfo1.Verb = "RunAs" '*** This is for UAC mode ***
' 中略
Dim process As System.Diagnostics.Process =
       System.Diagnostics.Process.Start(pinfo1)

これを利用した su として以前 wouldyou.exeというものを作ったが、後述の powershell を使った方法の方で用が足りるので、今はほとんど使っていない。

バッチファイル&PowerShell

.NET Framework と同じ方法がとれるが、COM を使った方法の方がお手軽。COM を使っているので、当然ながら JScript/VBScript でも同じことができる。が、起動ロゴが出たりするので、やはり、PowerShell が最適解だろう。

@setlocal
@if not "%1" == "" @set "ARG=/c %*"
powershell "(New-Object -Com Shell.Application).ShellExecute('cmd',$Env:ARG,'','runas')"
@endlocal

これのすごくよいところは1行コピペしてしまえば、それだけで用が足りる点だ (バッチファイル1枚の中で完結する)。

真の sudo を目指して

これらを利用して、su/sudo 的なものは比較的簡単に実現できる。自分はこれで満足していたが、コマンドプロンプトで実行する際、管理者権限を得たコマンドラインは別のコンソール窓になってしまうという点があり、これを使いにくく感じる向きも多いようだ。

ということで、それを解決した sudo も登場している。

実装の仕方を見ると、管理者権限で動くプロセスを別に立ち上げて、そちらとパイプラインを結んで、それまで使っていたコンソールに管理者権限で実行しているプロセスの標準入出力を引用しているようだ(← あまりソースを真剣に読んでいない)

なお、これらを NYAGOS で動かしたい時は NYAGOS内蔵の sudo を無効にするため、

  • ~\.nyagosnyagos.alias.sudo="sudo.exe"

もしくは

  • ~\_nyagosalias sudo=sudo.exe

としよう(.exeをつければ内蔵コマンドとみなされない)。nyagos の sudo を使わないのは、それが新しいコンソールを起動してしまうためだ(一応、廃止も検討している)

以上