相変わらず、騷しいう~さん
momiji

「旦那、今回は“連携ネタ” だそうですね?」
『そやねん、前回はキツかったんで今回は楽させてもろた』
『しかし、これを押さえとくとイザという時に役に立つやろ!』

「長いパス」対応の組み込みコマンド (nanoShell Tools)

1. 指定されたフォルダまたはファイルを複写する
 int copy( <folder[/wildcard]>, <folder> [," [sync]"])
 int copy( <file>, <file | folder> )
・名前 / サイズ / 日時 が異なるファイルのみ差分コピーする
・"sync" オプションを指定すると更に出力側にのみ存在するファイルを削除する(同期コピー)
   このメッセージは標準エラーに出力する (本体のメッセージは標準出力)

・ワイルドカード判定は、*、?、| を含む場合、
   または最後が ’.’ (ファイル名規約違反のため識別子として利用) の場合にワイルドカードと判断する
・ワイルドカードは、*→.*、?→.、.→\. に変換し、正規表現マッチする (大小文字を区別しない)
   こんな指定も可能です "[baz]*.txt|*.log"

・出力が (新規)フォルダの場合は、"foo/" と指定する

2. 指定されたフォルダまたはファイルを移動(リネーム)する
 int move( <folder[/wildcard]>, <folder>)
 int move( <file>, <file | folder> )

3. 指定されたフォルダまたはファイルを削除する
 int remove( <folder [/wildcard]> [," [root]"])
 int remove( <file>)
・ルートフォルダを指定する場合は "root" オプションが必要です

4. 指定されたフォルダをコンパクトなツリー表示する
 int tree( <folder[/wildcard]> [," [file]"])
・"file" を指定するとファイル名を表示する

5. ファイルを一覧表示する (listSegment)
 int ls( <folder[/wildcard]> [," [path] | [0 | , | k] [260 | no260]"])
・ファイル長、0: 編集なし、, : カンマ編集、K: 単位変換 (属性を表示するためにはいずれかを指定する)
・path: ファイル長の代わりに パス長を sort キーとし使用するための編集("%, 7d")をして表示する
・260: 長いパスを表示、no260: 短いパス、省略: 全て

・ls はシェル連携をを考慮してタブ区切りで出力しています (日時、長さ、パス)

6. シェル変数を設定する
 int set( [," [var=val] …"])
 int set()
・MAX_PATH=260 パスの最大長を指定する (tree、ls: 260、no260 オプションで使用)
・DISPLAY_WIDTH=40 パスを画面に表示する幅 (全体に影響)
・パラメータを省略すると現在値を表示します

7. 共通オプション
 リダイレクト: [12]>{1,2} path
・リダイレクトは ①後続オプションが存在する場合は、ファイル名をシングルクオートで囲む仕様です >'path'
   これは後続の一般オプションを食ってしまうためで、②オプションの最後に指定または、
   ③リダイレクト達のみの単独指定でも回避できます
   すなわち "/*オプション*/ 1>foo 2>bar /*ここにオプションは書けない*/ "

・パスは、DISPLAY_WIDTH 桁に切り詰めますが、リダイレクトの場合はそのまま出力する

 リカーシブ: noRecursive
・オプションは大小文字を区別しないで前方一致で比較する (コマンド内でユニークなら良い)
   "foo", "bar" では煩雑になるため "foo bar" 形式を推奨する
   否定オプション noRecursive はマイナスオプション -r[ecursive] に短縮可能です

8. 復帰コード
 0: 正常、1: インフォーメーション、2: ワーニング(マゼンタ)、3: エラー(赤)
・重大エラーは、throw するためあまり意味を持た持たないため、警告に使用しています

9. 外部コマンド呼び出し
 int system("<command> [<args...>] [>redirect]")

10. PowerShell 呼び出し
 int system("PowerShell <command> [<args...>] [>redirect]")

11. コマンドプロンプト呼び出し
 int system("CMD /C <command> [<args...>] [>redirect]")

※ ソースコード: plus/io/NanoTools.java

CHANGELOG

  • 2022-12-28 sort コマンドを Android nanoShell から持って来た
  • 2022-12-28 CSV ツールにカラム削除(del)機能を追加した
  • 2023-01-01 ls コマンドに path オプションを追加した
  • 2023-01-05 set (シェル変数の設定)コマンドを追加した

サンプルスクリプト

Step 1 - ls コマンドの出力をパス長の逆順ソートする

※ ls 呼び出し
## nanoCSV.awk [redirect] input
#
var redirect = "/dev/stdout" # Output file
BEGIN {
   print "\e[92m ls コマンドの出力をパス長の逆順ソートする\e[m"
   input = ARGV[1]
   ls(input, "p", ">" redirect)
}
※ make ファイル
 $(PLUS) sample/nanoCSV.awk -v redirect=./Tcsv01.tmp "X:/"
※ ls コマンドの出力
22/10/18 13:21   15 X:/Hello/你好.txt
22/11/28 18:29   10 X:/lib.zip
23/01/02 14:55 463 X:/Long/A---+----1----+----2----+----3----+----4----+----5----+----6----+----7….txt
22/12/11 16:19   20 X:/TEMP/src/java.zip
23/01/01 22:06   12 X:/timeStamp
※ 時刻の次(2番目)のカラムがパス長です
※ 出力結果は手動で切り詰めています

Step 2 - CSV ツールで SORT キーを付加する

※ CSV ツール
## csv.awk [-Fs] [-v OFS=s] [-v col=] [-v fmt=] [-v del=] file...
#
# F = " " # Input Field Separator (FS) (" " means /[ \t]+/)
# OFS = " " # Output Field Separator (OFS)
var col = "0" # c1 [,c2...] Output Field selection
var fmt = "" # Output format specification (ex."04d, 4d,-4s")
var del = "" # d1 [,d2...] Delete column
var stdout = "/dev/stdout" # Output file
BEGIN {
   len1 = split(del, DEL, ",")
   len2 = split(col, COL, ",")
   split(fmt, FMT, ",")
}
   {
   for (i = 1; i <= len1; i++) {
       $(DEL[i]) = "" # Delete column
   }
   x = ""
   for (i = 1; i <= len2; i++) {
       k = $COL[i]
       if (FMT[i] != "") k = sprintf("%" FMT[i], k)
       x = x k OFS
   }
   gsub(OFS "+", OFS, x) # Cleanup OFS
   gsub("^" OFS "+|" OFS "+$", "", x) # Trim OFS
   print x > stdout # GAWK extension
}
※ make ファイル
 $(PLUS) sample/csv.awk -F\t -v OFS=\t -v col=2,0 ./Tcsv01.tmp >./Tcsv02.tmp
・ ’col=2,0’ で 2番目のカラム(パス長、キー)と 0 (レコード全体)を出力する
・ 今回は編集済みのため使用しませんが、数値キーの場合は編集オプション (fmt="07d")が必要です
※ CSV ツールの出力
  15 22/10/18 13:21   15 X:/Hello/你好.txt
  10 22/11/28 18:29   10 X:/lib.zip
463 23/01/02 14:55 463 X:/Long/A---+----1----+----2----+----3----+----4----+----5----+----6----+----7….txt
  20 22/12/11 16:19   20 X:/TEMP/src/java.zip
  12 23/01/01 22:06   12 X:/timeStamp

Step 3 - SORT する

※ SORT コマンド
## sort [-r] [-i] [-u] file...
#
var stdout = "/dev/stdout"
val list = new ArrayList()
BEGIN {
   reverse = ignoreCase = unique = ""; option(1); option(2); option(3)
}
   {
   var key = $0; gsub("[\n\t]", "\r", key)
   if (ignoreCase != "") key = tolower(key)
   list.add(key "\n" $0)
}
END {
   Collections.sort(list)
   z = "\t"
   if (reverse == "") {
       for (x in list)
       printList(x)
   } else {
       for (i = list.size() - 1; i >= 0; i--)
           printList(list.get(i))
   }
}
function printList(x) {
   if (x != z || unique == "") {
       var w = x; sub(/^[^\n]*\n/, "", w)
       print w >stdout
       output += 1
   }
   z = x
}
function option(i) {
   if (ARGV[i] == "-r") {
       reverse = ARGV[i]; ARGV[i] = ""
   } else if (ARGV[i] == "-i") {
       ignoreCase = ARGV[i]; ARGV[i] = ""
   } else if (ARGV[i] == "-u") {
       unique = ARGV[i]; ARGV[i] = ""
   }
}
※ make ファイル
 $(PLUS) sample/sort.awk -r ./Tcsv02.tmp >./Tcsv03.tmp
・ ’-r’ で逆順ソートする
※ SORT の出力
463 23/01/02 14:55 463 X:/Long/A---+----1----+----2----+----3----+----4----+----5----+----6----+----7….txt
  20 22/12/11 16:19   20 X:/TEMP/src/java.zip
  15 22/10/18 13:21   15 X:/Hello/你好.txt
  12 23/01/01 22:06   12 X:/timeStamp
  10 22/11/28 18:29   10 X:/lib.zip

Step 4 - SORT キーを剥がす

※ make ファイル
 $(PLUS) sample/csv.awk -F\t -v OFS=\t -v del=1 -v col=0 ./Tcsv03.tmp >./Tcsv04.tmp
 $(PLUS) sample/cat.awk ./Tcsv04.tmp
・ CSV ツールでキーを削除し(del=1)、残すカラムは(col=0、全体)で指定する
・ CAT コマンドで結果を表示する(コードは覗いて見てください ^^)
※ CSV ツールの出力
23/01/02 14:55 463 X:/Long/A---+----1----+----2----+----3----+----4----+----5----+----6----+----7….txt
22/12/11 16:19   20 X:/TEMP/src/java.zip
22/10/18 13:21   15 X:/Hello/你好.txt
23/01/01 22:06   12 X:/timeStamp
22/11/28 18:29   10 X:/lib.zip

スクリーンショット

path1 path2

まとめ

『どや、シンプルやろ!』
「そうですね、これが旦那流ですか?」『そや、ジョブズ君も言うとったで』

「シンプルであることは、複雑であることよりもむずかしいときがある。物事をシンプルにするためには、懸命に努力して思考を明瞭にしなければならないからだ。だが、それだけの価値はある。なぜなら、ひとたびそこに到達できれば、山をも動かせるからだ。」

オープンソース

検証環境

  • Windows 10
  • Windows Terminal (オープンソース)、Windows PowerShell (オープンソース)
  • GNU Make (オープンソース)
  • Java 19 (オープンソース)
  • AWK˜plus for Java (オープンソース)

インストール

  1. Java をダウンロード(環境を汚さない .zip 版を推奨、複数の Javaもインストールできます)「Java Downloads」
  2. AWK~plus をダウンロード「AWK~plus for Java」 (コマンドを添付しています)
  3. AWK~plus フォルダ中の makefile の JAVAHOME 変数に Javaホームパスを設定する。

実行

ターミナルを開き、AWK~plus フォルダをカレントディレクトリにして、と入力する。

「Table of contents」 2023.01.05