まともな foreach が使えるようになります

次のバージョンから、環境変数の置換構文を組み合わせれば、下記のようなことができます。

$ foreach i *.go ; echo move %i% %i:go=goo% ; end
move bindata.go bindata.goo
move builtinfunc.go builtinfunc.goo
move complete_lua.go complete_lua.goo
move complete_var.go complete_var.goo
move loadscr.go loadscr.goo
move lua.go lua.goo
move lua_bindkey.go lua_bindkey.goo
move lua_cmd.go lua_cmd.goo
move nyagos.go nyagoos.goo
move option.go option.goo
move prompt.go prompt.goo
move stream.go stream.goo

諸般の事情で、環境変数の置換のタイミングが、 CMD.EXE や /bin/sh と違っていてアレですが、まぁ、便利なので、気にしないことにしましょう。

ソース的には、まだ master ブランチに入ってなくて、develop ブランチの方になります。

NYAGOS、かんたん設定

NYAGOS、かんたん設定 Windows向け拡張コマンドラインシェル:NYAGOS (Nihongo Yet Another GOing Shell) の個人設定はホームディレクトリに .nyagos というテキストファイル(Luaスクリプト)を作成して行います。

Windows の場合ホームディレクトリは一般に C:\Users\(ユーザ名) というフォルダーです。環境変数 HOME に別のフォルダーを定義しておけば、そちらへ変更することも可能です。

なお、メモ帳などで .nyagos という名前で保存しようとすると、勝手に .nyagos.txt という名前に変えられてしまいます。一旦、.nyagos.txt で保存してから、後からエクスプローラーで .nyagos. 等とわざと末尾に余分の . をつけた形にリネームしてください。末尾の .エクスプローラーに対して「拡張子が無い」ということを明示するためのものです。リネーム後に再表示させると、末尾の . が取れているのが分かると思います。

環境変数の設定

環境変数は設定ファイル .nyagos の中で、nyagos.env.変数名 という名前の変数に定義を代入することで設定できます。

たとえば PATH に C:\bin を加えたい時は

nyagos.env.path = nyagos.env.path .. ";C:\\bin"

と .nyagos に記入してください。Lua言語では「..」(ピリオド二つ)が文字列の連結の演算子です。\ は文字列の中なので \ とエスケープします。

なお、NYAGOS の紹介記事で set { ... } という記法を紹介しているものがありますが、これも間違いではありません。set は nyagos.exe と隣接したフォルダー nyagos.d の中の aliasandset.luaというアドオンファイルの中で定義されている便利関数です。

エイリアス(別名)の定義

エイリアス.nyagos の中でnyagos.alias.エイリアス名 という名前の変数に定義を代入することで設定できます。

たとえば「ls -l」を「ll」でも実行できるようにするには

nyagos.alias.ll = 'ls -l $*'

などと .nyagos の中に記載します。$*エイリアスに渡された引数を全て引用するという意味です。他に第一引数の $1,第二引数の $2 …などもあります。(これらのマクロがない場合は自動で勝手に末尾に $* があるとみなされます)

エイリアスは文字列で設定するだけでなく、Lua の関数にリンクさせることもできます。

nyagos.alias.printarg = function(args)
    for i=1,#args do
        print(args[i])
    end
end
% printarg x y a d
x
y
a
d

NYAGOS にはいろいろな機能がありますが、とりあえず環境変数の変更やエイリアスなどが使えれば、必要最小限のカスタマイズはできると思います。

vimとバッチファイルでtwitter と mstdn.jp にコマンドプロンプトから同時投稿する

dondon.cmd を実行すると vim が起動する。

  • 投稿したい時は「:eq
  • キャンセルする時は「:cq」か、ゼロバイトにしてから「:eq

dondon.cmd

@echo off
setlocal
set "DON=%TEMP%\don.txt"
type nul > "%DON%"
vim -c "e ++enc=utf8" "%DON%"
if errorlevel 1 exit /b
for %%I in (%DON%) do set "SIZE=%%~zI"
if %SIZE% LEQ 3 exit /b
twty -ff "%DON%"
mstdn toot -ff "%DON%"
endlocal

ここ近年、Go で作った UNIX っぽい Windows のツール

ANSI/UTF8自動判別と書いているは行単位で判断しているので、混在しててもだいたい大丈夫

神Excel にテキストファイルを流し込むスクリプト書いたよー

godexcel.cmd C3 AA30 tmp.xls ./GodExcel.cmd

という感じで実行すると、神 Excel「tmp.xls」の C3~AA30 の領域に、自分自身(GodExcel.cmd) の内容を展開して

f:id:zetamatta:20171123225106p:plain

という感じになる。

GodExcel.cmd

@set args=%*
@powershell "iex( (@('','','')+(cat '%~f0'|select -skip 3))-join[char]10)"
@exit /b %ERRORLEVEL%

$std_cell_width = 1.50

function Split-LikeShell($s){
    $rx = [regex]'"[^"]*"'
    while( $true ){
        $m = $rx.Match($s)
        if( -not $m.Success ){
            break
        }
        $left = $s.SubString(0,$m.Index)
        $right = $s.SubString($m.Index+$m.Length)
        $mid = (($m.Value -replace " ",[char]1) -replace '"','')
        $s = $left + $mid + $right
    }
    ($s -split " ") | ForEach-Object{ $_ -replace [char]1," " }
}

$args = (Split-LikeShell $env:args)

function Conv-RC($str){
    $i = 0
    $str = $str.ToUpper()
    $col = 0
    while( $i -lt $str.Length ){
        $index = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".IndexOf($str.substring($i,1))
        if( $index -lt 0 ){
            break
        }
        $col = $col * 26 + ($index+1)
        $i++
    }
    $row = 0
    while( $i -lt $str.Length ){
        $index = "0123456789".IndexOf($str.substring($i,1))
        if( $index -lt 0 ){
            break
        }
        $row = $row * 10 + $index
        $i++
    }
    return @($row,$col)
}

$left = 1
$top = 3
$right = 200
$bottom = 200

if( $args.Length -lt 2 ){
    Write-Output `
        "Usage: godexcel.ps1 [LeftTop] [RightBottom] ExcelFile TextFile..."
    exit
}

if( $args.Length -ge 1 -and $args[0] -match "^[A-Za-z]+[0-9]+$" ){
    $rc = Conv-RC($args[0])
    $top = $rc[0]
    $left = $rc[1]
    $args = $args[1..($args.Length-1)]
    if( $args.Length -ge 1 -and $args[0] -match "^[A-Za-z]+[0-9]+$" ){
        $rc = Conv-RC($args[0])
        $bottom = $rc[0]
        $right = $rc[1]
        $args = $args[1..($args.Length-1)]
    }
}

$excel = New-Object -ComObject Excel.Application
$cellwidth = @{}

try{
    $excel.Visible = $true
    
    $filename = [System.IO.Path]::GetFullPath($args[0])
    [System.Console]::WriteLine("Excel=$filename")
    if( Test-Path $args[0] ){
        $book = $excel.WorkBooks.Open($filename)
        $new = $false
    }else{
        $book = $excel.WorkBooks.Add()
        $new = $true
    }
    $sheet = $book.ActiveSheet

    $row = $top
    for($i = 1 ; $i -lt $args.Length ; $i++){
        Get-Content $args[1] |
        ForEach-Object {
            if( $row -le $bottom ){
                $count = $_.Length
                $col = $left
                for($j=0 ; $j -lt $count ; $j++){
                    if( $col -gt $right ){
                        $col = $left
                        $row++
                    }
                    if( $new -and -not $cellwidth.ContainsKey($col) ){
                        $sheet.Columns($col).ColumnWidth = $std_cell_width
                        $cellwidth[$col] = $true
                    }
                    $sheet.Cells.Item($row,$col) = $_.substring($j,1)
                    $col++
                }
                while( $col -le $right ){
                    $sheet.Cells.Item($row,$col) = ""
                    $col++
                }
                $row++
            }
        }
    }
    if( $new ){
        $book.SaveAs($filename)
    }
}finally{
    $excel.Quit()
}

# vim:set ft=ps1:

PowerShell で、二つのフォルダーのファイル構成を比較するコマンドを作る

dirdiff.cmd

@set "arg1=%~1" & set "arg2=%~2"
@powershell "iex((@('','','')+(cat '%~f0' | select -skip 3))-join[char]10)"
@exit /b %ERRORLEVEL%

$md5 = New-Object System.Security.Cryptography.MD5CryptoServiceProvider

function Get-MD5s($dir){
    $md5s = @{}
    Get-ChildItem $dir |
    Where-Object { $_.Mode[0] -ne "d" } |
    ForEach-Object {
        $data = [System.IO.File]::ReadAllBytes($_.FullName)
        $bs = $md5.ComputeHash($data)
        $md5s[ $_.Name ] = `
            ([System.BitConverter]::ToString($bs).ToLower() -replace "-","")
    }
    return $md5s
}

$dir1 = $env:arg1
$dir2 = $env:arg2

$hash1 = (Get-MD5s $dir1)
$hash2 = (Get-MD5s $dir2)

foreach($p in $hash1.Keys){
    if( $hash2.ContainsKey($p) ){
        $private:val1 = $hash1[$p]
        $private:val2 = $hash2[$p]
        if( $val1 -ne $val2 ){
            Write-Output ("{0} differ" -f $p)
            Write-Output ("  {0} {1}" -f $val1,(Join-Path $dir1 $p))
            Write-Output ("  {0} {1}" -f $val2,(Join-Path $dir2 $p))
        }else{
            Write-Verbose ("{0} same" -f $p)
        }
    }else{
        Write-Output ("{0} not found" -f (Join-Path $dir2 $p))
    }
}

foreach($p in $hash2.Keys){
    if( -not $hash1.ContainsKey($p) ){
        Write-Output ("{0} not found" -f (Join-Path $dir1 $p))
    }
}

# vim:set ft=ps1:
  • 最初の3行は、PowerShell をバッチファイル化するためのまじない
  • 二つのディレクトリの直下にあるファイルの md5連想配列に格納して、比較するだけ

こういうのは別のバッチファイルから呼び出したいから、Goで作った方がいいな…と後から思いました。まる

追記

Goで作ってみました。https://github.com/zetamatta/experimental/blob/master/dirdiff/main.go

(補足)git で特定の2つのフォルダーを使ってる履歴のみ残して、後は消す

最初の commit だけは argf , seek フォルダーに関わってなくとも残ってしまう(あとで revert した)

git rebase -i -root で、最初の commit も削除できた。