標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

PowerShell での項目の並べ方整理

コマンドを書く場合は「空白」で区切る。

  • 関数・外部コマンドに関係なく、引数リスト全体を囲む「括弧」は要らない。
Write-Verbose -Message "$ go build"

コマンドの引数に「式」を書いて評価させる場合は、その引数1個を「括弧」で囲む

  • 囲まないと、個別のトークンがそのまま、コマンドに渡される
Write-Verbose -Message ("Found {0}" -f $exename)

配列リテラルは要素をカンマで区切る

        foreach( $p in @(`
            "nyagos.exe",`
            "nyagos.syso",`
            "version.now",`
            "mains\bindata.go",`
            "goversioninfo.exe",`
            "go-bindata.exe" ) )
        {

※ 逆クォート一つは、行継続用のエスケープ文字

連想配列リテラルは代入式をセミコロンで連結する

PowerShell連想配列(ハッシュ)によると「@{one = 1; two = 2; three = 3}」と書けばよいらしい(手抜き)

ファイルサーバ名が変わったので、リンク切れした「お気に入り」を修復

Windows7エクスプローラで出てくる「お気に入り」は、%USERPROFILE%\Links というフォルダーにショートカットが全部並んでいる。 PowerShell を使って全部リネームしよう、そうしよう。

$fromName = "\\旧サーバ名\"
$toName = "\\新サーバ名\"

$wsh = New-Object -ComObject WScript.Shell
Get-ChildItem (Join-Path $env:userprofile "Links") | %{
    if( $_.Extension -eq ".lnk" ){
        $shortcut = $wsh.CreateShortcut($_.FullName)
        $target = $shortcut.TargetPath
        if( $target.StartsWith($fromName) ){
            $newTarget = $toName + $target.substring($fromName.Length)
            if( Test-Path $newTarget ){
                Write-Output ("    " + $target)
                Write-Output ("--> " + $newTarget)
                $shortcut.TargetPath = $newTarget
                $shortcut.Save()
            }
        }
    }
}

Write-Host と Write-Output ではリダイレクトした時の改行コードが変わってしまう

(※ 2017.09.15追記: stknohg さんから、いろいろ技術的な補足をいただきました。詳しくはコメント欄を参照ください)

  • Write-Host の出力を、PowerShell の呼び出し元でリダイレクトすると、改行コードが LF になる。
  • CRLF にするには Write-Output を使わねばならない

host.ps1:

Write-Host "A"
Write-Host "B"
$ host.ps1 > host.txt
$ dumphead.exe host.txt
41 0A 42 0A

(※ 9/15 追記。NYAGOS から実行したので省略していますが、これ頭に powershell -ExecutionPolicy RemoteSigned -file が入っています)

output.ps1:

Write-Output "A"
Write-Output "B"
$ output.ps1 > output.txt
$ dumphead.exe output.txt
41 0D 0A 42 0D 0A

(※ 9/15 追記。同じく頭に powershell -ExecutionPolicy RemoteSigned -file が入っています)

最初、vim とかで見てたので気づきませんでしたが、 プログラムを提出後、メモ帳で見たら Write-Host で出したプログラムの改行が消えて見えたので、 慌ててしまいました。


Write-HostとWrite-Outputの違い - しばたテックブログ を読みました。本来は:

  • Write-Host はコンソールに出力するコマンド
  • Write-Output は標準出力に出力するコマンド

で、その辺の違いが出力に現れているのかもしれませんね(おざなり)

以上

OneNote 2016 を使ってみた

evernote のデータを本格的に移行しようとは思わなかったが、ちょっとお試しで OneNote を使ってみた。

OneNote の種類にとまどった

会社PC の Office2010 に入ってる OneNote と、Windows10 に最初から入っている OneNote のユーザインターフェイスが微妙に違う。 単にバージョンが違うからかと思って、フリー版の最新であるはずの OneNote を入れてみたが、それでもなお Windows10 の OneNote とそっくりにならない。

どうやら、Windows10 にプリインストールされている「OneNote(無印)」はモバイル向けの OneNote で、 フリー版の OneNote =「OneNote 2016」とはまた違うバージョンとは違うもののようだ。 使い勝手では Office 2016 が一番使いやすいので、会社PC、自宅PCともに OneNote 2016 に統一することにした。

なお、OneNote 2010 もちょっと使ってデータがあったのだが、OneNote 2016 にデータを移すには、 サブスクリプションの購入が必要のようだ。大したデータでもないので、すっぱり捨てた。

evernote との違い

  • OneNote は端末数に制限なし。evernote は無料は2台まで

    • OneNote はパスワード付きのフォルダーが作れるが、PC でしか読むことができない。モバイル端末ではパスワードを知っていてもフォルダーを開けない(※@unagix さんからの指摘を受けて追記:開けないのは、Fireタブレットだけかも?)
  • OneNote はフォルダー管理、evernote はタグ管理

    • OneNote は多層の階層構造がある
      • ノートブック (>セクショングループ)>セクション>ページ>サブページ
      • サブページの下にサブページは作れるが、折りたたむ事ができないので、あまり意味がない
    • evernote にも階層構造はあるが、ノートブック>ノートと1階層のみ
      • そのかわり、多数のタグをつけることができる
  • 両方とも検索すれば、まぁ、記述を探すことには不便がない。

  • ゴミページがたくさんできてしまうような使い方の場合、階層構造がある方がゴミページを目立たない方へ移動させられるということで、精神衛生上よいような気がする(そんなことしても「検索」すれば出てきてしまうのではあるが)

その他

  • OneNote はオフィスファミリーの一員であるため、Excel とかと相性がよいような気がする。
  • OneNote にはファイルを添付できるのはよいのだが、ファイル名を全部表示してくれないのは非常に残念。拡張子を表示してくれないのはだめだろう。
  • 自分は、自分のマシンのファイル散乱対策に結構使えると思ってる。Thunderbird のメールを直接ドラッグ&ドロップも出来るので、メール+関連ファイルを1つのページにおさめておくことができる(evernote でも多分できるだろう)。

とりあえず、以上

本当に 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

というコード書いた。