それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Shift押しながらの右クリックメニュー(Shellex)は認知度が低すぎる。

Windowsでは、Shift+右クリックメニューで表示内容が変わる。 MacOS(旧OSX)では、Optionを押すと右クリックメニューが変わる。

Shift + 右クリック( ShellEx)

右クリックメニュー(コンテキスト・メニュー)はすべて表示される。

右クリックメニュー(Shell)

右クリックメニューは通常使わないものを消しておくとスッキリする。

Shift+右クリック、知名度低すぎ?

色んな人のWindowsの使い方を見る機会があるのだけど、ほとんど認知されていなのが、Shift+右クリックメニュー。

ExcelでさえShift+右クリックを諦めた。

昔は、ExcelのメニューもShift+右クリックがあった。貼り付けオプション(形式選択して貼り付け)が、それだった。エクセルもShift+右クリックだった。しかし、認知されなくて、アイコン化された。

このエクセルのShift+右クリックメニュー廃止が、成功例としてエクスプローラーのメニューの今後の方向性になるのかと思ってたらWin11でそうだった。

win11 の方向性は悪くないが。

Win11ではどうなったのだろうか。というと、更に劣化していそうである。

「ユーザーが右クリックメニューを編集する機能」が必要だったと思う。

個人用PCでは問題ないが、レジストリを触れないため職場PCなどで不自由が生じていることは多分解決しない。

Shift+右クリックメニューで表示されるベンチ入り選手と、右クリックで表示されるスタメンの入れ替え機能がWin11には欲しかった。

アイコン表示じゃないんだよ。本当に必要なのは。

ベンチ入り・スタメン入れ替え機能が欲しかった。

本当にほしいのは、右クリックメニューの編集機能だったと思う。スタメンとベンチ入りと2軍の編集機能だったと思う。

しかし、右クリックメニューのClassesは多岐にわたるため、一般ユーザー向けではないと判断されたのだろうか。レジストリ変更を伴うから仕様変更が困難だと判断されたのだろうか。残念である。

Win10でユーザーチョイスで関連付けを変更ができるようになったのだから、右クリックメニューの編集機能が欲しかったのよねぇ。

Shift+右クリックメニューの編集方法

ShellMenuView というソフトウェアが、手軽に編集をさせてくれる。

https://www.nirsoft.net/utils/shell_menu_view.html

コレを使うと、スタメン落ち・2軍行きを切替え可能だ。

  • 該当項目をSet Extended Mode するとShift+右クリック(スタメン落ち)
  • 該当項目をDisable Selected Item するとメニューから抹消(2軍行き)

とできる。

項目が見つからないときは検索する

スタメン落ちとはShift+右クリックメニューが必要な項目とすることで、Shell->Shellex に変えるレジストリ操作に相当する。

2軍行きとは、メニューを非表示にする操作を意味し、項目は残すが、LegacyDisableキー追加するレジストリ操作に相当する。

LegacyDisableではなく、今どきは HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked にエントリを追加するほうが良いかもしれないが。

レジストリ操作で消せるもの消したい。

レジストリ操作で消せるもの消してスッキリしたい

消せるものは極限まで消しておきたい。

Create Shortcut(ショートカット作成)は消せないし、送るメニューも消すことは出来ない。

レジストリで消せるものは限界まで消しておきたい。

ドキュメント類の制作の邪魔になるメニュー項目。

私は、たまに操作説明手順の書類を作るのだが、右クリックメニューが多すぎると、操作説明手順書が作りにくい。

余計なもの(VLCVSCode)が右クリックメニューにあると、スクショを編集したり、紛らわしい右クリックメニューが入っていると、操作手順を変えたものを説明する必要が出てきて面倒くさい。

スッキリしたメニューがよい

右クリックメニューに気軽に登録してくるマナーの悪いアプリケーションは嫌いです。

私達が本当に必要な右クリックメニュー

私達が本当に「必要な」右クリックメニューとは、ファイルを開くアプリケーションを選べることではないか。

例えば、.gitignore を開けるもの

例えば、package.json を開けるもの

例えば、Zipファイルを開くアプリケーションを選ぶ

こういうことではないか。右クリックメニューにアプリケーションが居座るのって整理出来てなくないか。

レジストリの探索方法

shmnview.exe でも消せない事があるので、そのときはレジストリを探索する。その場合知っておくべきことがある。

関連付けや右クリックメニューは、HKEY_CLASSES_ROOT(HKCR) になるが、HKCRはHKCUとHKCMを合成して表示するように再計算されたもの。

コレを知っておく必要がある。

HKEY_CLASSES_ROOT = HKEY_CURRENT_USER + HKEY_CURRENT_MACHINE

該当項目のなかで、ファイルタイプ以外にDrive , Directory , Directory/Background , DesktopBackground がエクスプローラー用に存在している。

ShellExとShellを入れ替えれば、目的は達成される。

昔は、移動がとてもめんどくさかったが、今は、Powershellのコマンドでとても手軽になった。

例えば、VSCodeが「Drive」のメニューにすら出てるのをやめさせたい。

$from = 'HKLM:\SOFTWARE\Classes\Drive\shell\VSCode' 
$to   = 'HKLM:\SOFTWARE\Classes\Drive\shellex\VSCode'
Move-Item -LiteralPath $from -Destination $to

パーミッションエラー

更に最近は、レジストリUACですら書き換えられない場合がある。

Ps はパーミッションエラーでもキーが存在しないメッセージを返すので、さらに面倒くさい。

Move-Item : Cannot delete a subkey tree because the subkey does not exist.

一つ上の階層を書き換える。

上位階層からサブ(下位)のパーミッションを上書きする。

例えば、HKLM\SOFTWARE\Classes\Drive\shell\Powershell の所有者は、SYSTEMだが、SYSTEMにフルコントロールが存在しない。

TrustedInstaller にはフルコントロールが存在する。

TrustedInstallerになるのはちょっと面倒くさいので。

上位の所有者を書き換えて、サブコンテナも合わせて上書きする。

HKLM\SOFTWARE\Classes\Drive\shell\Powershell の上位だから `HKLM\SOFTWARE\Classes\Drive\shell\ を上書きする。

もとの所有者がSYSTEMの場合に、SYSTEMを上書きするのは手間が多いので、とりあえず適当なユーザー(Adminなど)書き換えて、SYSTEMに戻す。戻すとき、連動してサブツリーの所有者が修正される。

パーミッションを開いて

所有者をAdminなどに書き換えて、

所有者をSYSTEMに戻す。

戻すときに、サブツリー(子孫コンテナ)も合わせて上書きされるので、フルコントロールが手中に返却される。

まとめて消す例

PowershellでまとめてShellExへ移動させる例

foreach( $path in "Drive", "Directory","Directory\Background" ){
  foreach ( $name in "WSL", "PowerShell", "git_gui", "git_shell", "cmd" ) {
    $from = "HKLM:\SOFTWARE\Classes\$path\shell\$name"
    $to   = "HKLM:\SOFTWARE\Classes\$path\shellex\$name"
    Move-Item -LiteralPath $from -Destination $to
  }
}

パーミッションの書き換えも、そのうちにPowershellで作りたい。

7zipでワンタッチ解凍(展開)をしたい。

ダブルクリックでデスクトップに解凍してほしい。

zip ファイルを中身を見て、必要ファイルを取り出すとか面倒くさい。

ダブルクリックで解凍して、エクスプローラーで表示してほしい。

特に、USBメモリやSambaのフォルダを開くときに、ダブルクリックでデスクトップへ展開が重要なのである。

レジストリで操作する。

前に、試したことで、レジストリをイジれれば、好きな箇所にフォルダを作成し展開できることはわかった。

解凍後にエクスプローラーで開けてほしい。

今度の欲求は、解凍後にフォルダが見えないという点だ。

解凍(展開・伸長)後に、エクスプローラーでフォルダを開いてほしい。

レジストリの登録を次のようにしたい。

7-Zip.desktop というProgIdを作り、zipの関連付けを変更する。

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\DefaultIcon]
@="\"C:\\Program Files (x86)\\Lhaplus\\LplsIcon.dll\",101"

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open\command]
@="\"C:\\Users\\takuya\\.app\\7zipToDesktop.exe\" %1"

[HKEY_CURRENT_USER\SOFTWARE\Classes\.zip]
@="7-Zip.desktop"

個々に必要なExeかスクリプトを作る

WSHやPSで書いても良かったんだけど。WindowsだしExe作ってやろうと思って、Windowsアプリケーションを書き始めた。

だいたい、書けた。

展開先の設定画面とかはコンパイルすればいいので、要らない。

ソースコード

シェルスクリプトで頑張ろうと思ったけど、WSHVBScript/JScript の未来が不透明だし、Powershellは面倒くさいし、いまさらcmd.exeを書く気になれなかった。

VisualStudioのC++なら、現代ではMicrosoftが無償で使わせてくれるので、超久しぶり書いたわ。

https://github.com/takuya/win-7zip-onclick/blob/master/7zipToDesktop/7zipToDesktop.cpp

// 7zipToDesktop.cpp : Defines the entry point for the application.
//
// License: GPLv3
// author : github.com/takuya
// modified : 2024-05-29
//
#include<iostream>
#include <Windows.h>
#include <filesystem>
#include <regex>

namespace fs = std::filesystem;
using string = std::string;
using wstring = std::wstring;

fs::path get_desktop() {
    char* env_var = nullptr;
    size_t size;
    _dupenv_s(&env_var, &size, "USERPROFILE");
    if (env_var == nullptr) {
        return "";
    }
    std::string UserProfile(env_var);
    return fs::path(UserProfile) / "Desktop";
}
string get_output_path(string src) {

    std::string dstDir = get_desktop().string();

    fs::path src_path(src);
    fs::path basename = src_path.stem();
    std::regex extensionRegex("\\.\\w+$");
    std::string basename_noext = std::regex_replace(basename.string(), extensionRegex, "");
    fs::path dstPath = fs::path(dstDir) / basename_noext;
    return dstPath.string();
}
void openExplorer(string path) {
    string application("explorer.exe");
    string out_dir = get_output_path(path);
    if (!fs::exists(out_dir)) {
        return;
    }
    HINSTANCE result = ShellExecuteA(nullptr, nullptr,
        application.c_str(),
        out_dir.c_str(),
        NULL,
        SW_SHOWNORMAL
    );
}
int extractToDesktop(string archivePath) {
    fs::path desktopPath = get_desktop();
    if (desktopPath.empty()) {
        return 1;
    }
    string args = archivePath;
    args = " x " + args + " -aos -o" + desktopPath.string() + "\\*";
    string cmd = R"(C:\Program Files\7-Zip\7zG.exe)" + args;
    wstring cmdW(cmd.begin(), cmd.end());
    LPCWSTR lpwargs = cmdW.c_str();
    LPWSTR  lpargs = const_cast<LPWSTR>(lpwargs);

    STARTUPINFO si = {};
    PROCESS_INFORMATION pi = {};
    if (CreateProcessW(NULL, lpargs, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    
    }
    else {
        std::cerr << "Failed to execute command." << std::endl;
        return 2;
    }
    return 0;
}


int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {

    if (lpCmdLine == NULL) {
        return 1;
    }
    // std::string を std::wstring に変換
    string args = lpCmdLine;
    extractToDesktop(args);
    openExplorer(args);
    return 0;


}

書くときに調べたメモ

Win32なんて超久しぶりなので、一つずつ調べて書いた。

  • シェルコマンドの実行方法
    • CreateProcess
    • std::system
    • ShellExecuteA
  • 環境変数の取得
  • ファイルパスの扱い
    • basename / stem
    • extname / replace extension
    • join / path
  • WinMainを使って画面なしExe
  • namespace
    • using
    • include
  • string
    • wstring と LPCWSTR
    • Raw 書式
    • LPCWSTR と LPCSTR の変換

特に、Windowsアプリケーションは文字列型キャストが本当に面倒くさい。c++で書いても、Cへキャストしないといけない。

文字列型の備忘録( 参考資料 )

Item 8-bit(ANSI) 16-bit ( Wide) Varies
character CHAR WCHAR TCHAR
string LPSTR LPWSTR LPTSTR
string (const) LPCSTR LPCWSTR LPCTSTR

略称がややこしいが、ConstとWideだけ覚えておけばいい。

  • LPCSTR は Long Pointer Const String
  • LPCWSTR は Long Pointer Wide Const String
  • LPSTR は Long Pointer String

CreateProcess の第1引数をいれると、第二引数が無視される仕様なのが、ハマりどころだった。

エクスプローラーの起動

#include<windows.h>
#include<iostream>

int main()
{
    LPCSTR application = "explorer.exe";
    LPCSTR parameters = "C:\\Users\\takuya\\Desktop";
    HINSTANCE result = ShellExecuteA(nullptr,nullptr,
        application, // アプリケーション
        parameters, // 引数
        NULL, // ディレクトリ
        SW_SHOWNORMAL // ウィンドウの表示状態
    );
    if ((int)result <= 32) {
        MessageBoxA(NULL, "explorer.exeの実行に失敗しました。", "エラー", MB_OK | MB_ICONERROR);
    }
    else {
        MessageBoxA(NULL, "explorer.exeは成功しました。", "成功", MB_OK | MB_ICONASTERISK);

    }
}

curl.exe の実行

#include<windows.h>
#include<iostream>

int main()
{
    //
    LPCSTR application = "C:\\Windows\\System32\\curl.exe";
    LPCSTR parameters = " -v https://g.co -o C:\\Users\\takuya\\Desktop\\out.html";
    //
    HINSTANCE result = ShellExecuteA(nullptr,nullptr,
        application, // アプリケーション
        parameters, // 引数
        NULL, // ディレクトリ
        SW_SHOWNORMAL // ウィンドウの表示状態
    );
    return (int)result;

}

ファイル(フルパス)から、拡張子を除去

#include<windows.h>
#include<iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    std::string pathString = R"(C:\Users\takuya\Downloads\sample - 2024 04.zip)";
    fs::path path(pathString);
    fs::path basename = path.replace_extension();
    std::cout << basename.string() << std::endl;
    return 0;

}

ファイル(フルパス)からファイル名(Basename)を取得

#include<windows.h>
#include<iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    std::string pathString = R"(C:\Users\takuya\Downloads\sample - 2024 04.zip)";
    fs::path path(pathString);
    fs::path basename = path.stem();
    std::cout << basename.string() << std::endl;
    return 0;

}

ファイル名とディレクトリ名を結合

#include<windows.h>
#include<iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    std::string pathString = R"(C:\Users\takuya\Downloads\sample - 2024 04.zip)";
    std::string dir = R"(C:\Users\takuya\Desktop)";

    fs::path path(pathString);
    fs::path basename = path.stem();
    fs::path dstPath = fs::path(dir) / basename;
    std::cout << dstPath.string() << std::endl;
    return 0;

}

環境変数の値を取得し、Desktopのパスを取得

#include<windows.h>
#include<iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    // 環境変数 USERPROFILE の値を取得
    char* env_var = nullptr;
    size_t size;
    _dupenv_s(&env_var, &size, "USERPROFILE");
    if (env_var == nullptr) {
        return 1;
    }
    std::string UserProfile(env_var);
    fs::path desktopPath = fs::path(UserProfile) / "Desktop";
    std::cout << desktopPath.string() << std::endl;
    return 0;

}

コマンド(エクスプローラー)起動をstd::string から

先程の例は、C++ というより、LPCSTRを使ったCサンプルなので。

もう少し、c++ らしく。

#include<windows.h>
#include<iostream>

namespace fs = std::filesystem;
using string = std::string;
int main()
{
    string application("explorer.exe");
    string parameters = R"(C:\Windows)";
    HINSTANCE result = ShellExecuteA(nullptr, nullptr,
        application.c_str(),
        parameters.c_str(), 
        NULL, 
        SW_SHOWNORMAL 
    );

    return 0;

}

もっと簡単に、std::systemを使う

#include<string>
using string = std::string;

int main(){
  string application("explorer.exe");
    string parameters = R"(C:\Windows)";
    string cmd = application + " " + parameters;
    std::system(cmd.c_str());
}

プロセス実行して待つ

system は、終了待ちする。それいいけど、CreateProcess系Windowsの正統派かもしれない。

Windowsの文字列変換が全然わからないので、不慣れなWindowsプログラミングでCreateProcessは文字列変換が地獄かもしれない。

#include<iostream>
#include <Windows.h>

//namespace fs = std::filesystem;
using string = std::string;
using wstring = std::wstring;
int main()
{
    LPCWSTR programPath = L"C:\\Windows\\System32\\cmd.exe";
    LPWSTR  args = const_cast<LPWSTR>(L"/c echo Hello world!");

    STARTUPINFO si = {};
    PROCESS_INFORMATION pi = {};
    if (CreateProcessW(programPath, args, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
    else {
        std::cerr << "Failed to execute command." << std::endl;
    }
    return 0;
}

何も起きない(ウィンドウ開かない)アプリケーション

#include <windows.h>

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd){
    return 0;
}

メッセージボックスだけ

#include <windows.h>
#include <iostream>

using wstring = std::wstring;


int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd){

    wstring msg = L"ハローワールド";
    MessageBox(NULL, msg.c_str(), L"Command Line Arguments", MB_OK | MB_ICONINFORMATION);
    return 0;
}

WinMainでコマンドラインの引数を取ってメッセージボックスにだす

#include <windows.h>
#include <iostream>

using string = std::string;
using wstring = std::wstring;


int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_  HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd){
    // 引数無しは、動作無し。
    if (lpCmdLine == NULL) {
        return 1;
    }
    // std::string を std::wstring に変換
    string commandLineStr(lpCmdLine);
    wstring commandLine = std::wstring(commandLineStr.begin(), commandLineStr.end());
    MessageBox(NULL, msg.c_str(), L"Command Line Arguments", MB_OK | MB_ICONINFORMATION);

    return 0;
}

参考資料

nginx の proxy_redirectでlocationヘッダを書き換える

nginx の proxy_redirectを知りました。

Locationを書き換えることができます。

シンタックス

proxy_redirect from to 

実例

proxy_redirect http://localhost:800 https://$server_name

効果

nginx のリバプロ先からLocation・Refreshが返されたとき、その転送先のアドレスを書き換える。

殆どの場合は、リダイレクトはnginxが書き換えてくれるが、websocketやポート番号が異なると書き換えてくれないので、sub_filterや proxy_redirectを使って書き換える必要がある。

正規表現も使える。基本的に proxy pass reverse 的な動きをする。ただし、いつもの通りコンテンツ(Body)は書き換えの対象外。

コンテンツ内部を書き換えるなら、別の方法(sub_filter, body_filter_by_lua_block)を取る。

zip(圧縮)ファイルを、ダブルクリックで指定フォルダに展開したい。

lhapuls の指定フォルダに解凍(展開)が便利だった。

lhapuls は死にました. zip ファイルは存命ですが、ソフトウェアが脆弱性や、その他理由で絶滅しそうです。

lhapuls はソフトウェアとしては、機能が他に比べて劣るところもあるが、「指定フォルダに展開」して「エクスプローラーで開く」機能が便利で愛用していた。

ほかのソフトウェアでは、ネットワーク(SMB)のZipを開いて展開すると、同一フォルダ内部に書き込みに行く。そのため、ネットワークフォルダや、USBドライブでは速度が遅くなったり書き込みエラーで止まったりで大変不便だった。

ダブルクリックで、指定箇所に展開してくれるのが本当に便利だった。

ダブルクリックで指定フォルダに解凍(展開)を7-Zip で実現したい。

ダブルクリックで、デスクトップへzip ファイルを展開したい。

そのために、レジストリを作成する。

レジストリを構成する。

関連付けの変更

CURRENT_USERの.zip の関連付け先を 7-zip.desktop(これから作る) という名前にする。

ProgID 7-zip.desktop の作成

7-zip.desktopを作成する

次のキーを順番に作る。

HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\
HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\
HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open
HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open\command

コマンドを指定する。

作った、command キーHKCU\SOFTWARE\Classes\7-Zip.desktop\shell\open\command のdefault値に、次のようにコマンドを入れる。

"C:\Program Files\7-Zip\7zG.exe" x "%1" -oC:\Users\takuya\Desktop\*

コマンド指定のポイントは、-oパス名\* ( -o のあとにスペースなし)である。-o の直後のスペースは不要です。x は 展開を指定します。%1 はファイルパスが来ます。%1 はスペース付きのファイル名が来るので、ダブルクォーテーションで囲みます。

アイコンも指定しておく

アイコン用のキーを作成する

HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\DefaultIcon

デフォルト値に、次の値を入れた

"C:\Program Files (x86)\Lhaplus\LplsIcon.dll",101

今回は、サンプルのため、Lhaplus のアイコンを採用したが、別になんでもいい。

ダブルクリックで実行してみる。

ダブルクリックで実行して、デスクトップへ展開されることを確認。

レジストの例(コピペ用)

ProgIDのほう

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\DefaultIcon]
@="\"C:\\Program Files (x86)\\Lhaplus\\LplsIcon.dll\",101"

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open]

[HKEY_CURRENT_USER\SOFTWARE\Classes\7-Zip.desktop\shell\open\command]
@="\"C:\\Program Files\\7-Zip\\7zG.exe\" x \"%1\" -oC:\\Users\\takuya\\Desktop\\*"

関連付けの方

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Classes\.zip]
@="7-Zip.desktop"

快適になった

コレで、ある程度快適である。

USBメモリ内部でZip展開が行われて「ああああ、、」とならず、ネットワークフォルダで書き込みパーミッション無しで怒られることもなく、ダブルクリックでビューワーが開いてめんどうにもならない。

ただ、展開後のフォルダがエクスプローラーで開いてくれないのが難点である。

そこまで解決するには、PS1(パワーシェル)を作るか、exeをサクッと作るか、batファイル化、WSHを施さなくてはいけない。それは次回に持ち越し。

参考資料

僕の他にもダブルクリックで展開してくれよ。って思う人がいた。

AdguardHome のDNSキャッシュをパージ(クリア)をAPIからサクッと行う。

Adguard のキャシュ削除をやりたいと思った。

aguard home にはAPIが用意されている

https://github.com/AdguardTeam/AdGuardHome/blob/master/openapi/openapi.yaml

adguard のAPIを呼び出すcurl スクリプト

サクッと書いたら、こんな感じ。プログラミングするまでもないや。

adg-api.sh

#!/usr/bin/env bash

adg_pass=PASSWORD
adg_user=ADMIN_USER
adg_host=ADGUARD_HOME_IPv4

function adg_api(){
  adg_host=$1
  path=$2
  data=$3
  if [[ ! -z $data ]] ; then
    ret=$(curl --show-error --fail -s -X POST -H 'Content-type: application/json' -u $adg_user:$adg_pass -d "$data"  http://$adg_host/control$path );
    if [[ $? == 0 ]] ;then
      echo Success. >>/dev/stderr
      echo { \"result\" : \"$ret\"};
    else
      echo Failed.
      echo $ret
    fi
  else
    curl -s -X GET -u $adg_user:$adg_pass http://$adg_host/control$path
  fi

}

function main(){
  api_path=$1
  post_data=$2
  adg_api $adg_host $api_path $post_data
}

main $@;

API リクエストを叩けば、軽快に操作できる。

使い方(POST)

./adg-api.sh '/cache_clear' 'dummy'         

使い方例 (GET)

./adg-api.sh '/dns_info'

API を叩けばなにかするときになにかできる。

例えば、DDNSを更新したときにキャッシュクリアするとかね。

gitミラーリングを試す。gitlab の特定ブランチをgithub の指定ブランチへミラーリングしたい

gitlab の特定ブランチをgithub の指定ブランチへミラーリングしたい

開発中では、適当にコミットして適当にプッシュしたくないですか?

自分の作業内容がある程度まとまって git rebase などでコミットをまとめてから ブランチに切り出して、プッシュしたいじゃないですか。

作業内容のブランチでコミットメッセージを考えるのが面倒じゃないですか。

ローカル・ブランチでいいと思うんだけど、ローカルだけにデータ持つのが怖いじゃないですか。

mirror ブランチへpush したら github にプッシュしてほしい。

自動でブランチをミラーリングすればいいじゃないか。

そこで、作業用にはGitLabをつかって、見せられる状態に整理し、特定ブランチにプッシュ。そしたら Github にプッシュしたらいいじゃないですか。

それ、ローカルだけでできるじゃん?

2つレポジトリを作っておいて。

git remote add my-work ssh://my-git.example.com/takuya/project01
git remote add origin ssh://github.com/takuya/project01
## 作業中のコミット
git commit -m toda-saved
git push my-work work
## 整理したコミットをプッシュ
git push origin revised:branch/patch/1234

でも、レポジトリを使い間違えて、事故が起きやすいじゃないですか。

あと、git clone するたびに レポジトリの設定を書くのが面倒くさいじゃないですか。

そういうこと考えれば、レポジトリを完全に分けたいじゃないですか。

ミラーリングで解決できるか試す。

そこで、gitlab の特定のブランチにpushしたら、github の特定ブランチにミラーリングするようにしただろうだろうか。と思ったわけですよ。

つまり、Gitlab の workflow を使って、Gitlab からgithub の指定ブランチへミラーリングを自動実行できれば、楽になりそうじゃん。

準備

最終的に成果物を設置する github プロジェクト(レポジトリ)を作成。

そこに、デプロイ・キーを書き込み可能で作成する。

鍵を作成して

ssh-keygen -t ecdsa -f ./id_ecdsa
cat ./id_ecdsa.pub | pbcopy

書き込み可能にチェックして登録する

元レポジトリでミラーリングを入れる。

元になるプロジェクト(レポジトリ)をGitLabに作成して、デプロイキーを作成する

元レポジトリでCI/CDを作る

そして、プロジェクトのCI/CDのランナーを有効にして

GitLabでCIをするように設定する。

stages:
  - mirror


copy-to-github:
  stage: mirror
  tags:
      - takuya
  script:
    - echo Start mirroring
    - |
      ssh-keyscan -t rsa github.com >  ./known_hosts
      git switch mirror
      echo $GITHUB_DEPLOY_KEY
      cp $GITHUB_DEPLOY_KEY  id_ecdsa
      chmod 400 id_ecdsa
      (git remote | grep gh ) || git remote add gh git@github.com:takuya/mirror-test.git
      cat ./id_ecdsa
      cat ./known_hosts
      cat .git/config
      git -c core.sshCommand="ssh -i ./id_ecdsa -o UserKnownHostsFile=./known_hosts" push gh mirror
    - echo End mirroring
  only:
    - mirror  

これで、特定の名前(今回はmirror)にpush したら、github にもpush されるわけである。

git remote add origin https://my-git.example.com/takuya/project01
git push origin mirror

なんかすごいめんどくさいけど、github 側のブランチ名が大量に溢れかえることがなくなって非常に便利である。

整理整頓が大事。

大量ブランチが以前未整理の社内githubプロジェクトとか、高頻度で見かけますものね。

ブランチ名前を変えてプッシュ

名前を変えてブランチにプッシュしたいときは

git でローカルブランチを別名でpushする方法を使えばいい。

git push origin local_name:remoteName

ただ、この方法にも欠点があって、github~/.gitlab-ci.ymlが同期されてしまう。これはちょっと問題ですね。

gitlab を使うから、このような問題が発生するのであって、gogs やベアレポジトリ使って、プッシュ時のフック・スクリプトで対応すれば解決すると思うんだよね。っていうか作業用にGitlabのような高機能サーバーは要らんしね。

まとめ

今回使った同期(ミラーリング)のスクリプトのポイントは次の通り

## github.com のサーバー ホスト鍵を登録
ssh-keyscan -t rsa github.com >  ./known_hosts
## デプロイ用のSSH秘密鍵を登録。
cp $GITHUB_DEPLOY_KEY  id_ecdsa && chmod 400 id_ecdsa
## git remote を登録(すでに登録済みなら上書きしない。
(git remote | grep gh ) || git remote add gh git@github.com:takuya/mirror-test.git
## ミラーリングしたいブランチに切り替える。
git switch mirror
## git -c で ssh オプションを指定してい鍵とホスト鍵を使う。
git -c core.sshCommand="ssh -i ./id_ecdsa -o UserKnownHostsFile=./known_hosts" push gh mirror:mirror01

特に、git -csshオプションを指定して、ホスト鍵と秘密鍵を指定してる箇所がポイントになると思う。

sshなら、鍵さえクリアすれば、git コマンドを自動実行でpush / pull できるようになるので、自動実行が簡単になると思う。

もしhttps アクセスのときは、credential を作れば解決できるし、もう少し楽かも。

lua でhttp リクエスト

lua でhttp リクエス

local http_request = require("http.request")

-- HTTPリクエストを作成
local headers, stream = http_request.new_from_uri("https://g.co"):go()
local body = stream:get_body_as_string()
if headers:get ":status" ~= "200" then
    error(body)
end
print(body)

追加パッケージのインストール無しでパッケージ管理も使わずに使えた。

なんでだろうと疑問を感じたので、apt を調べたら。なんかapt でインストール済みだったみたい。

現在の apt の lua 環境

sudo apt list '*lua*' --installed  | cut -d '/' -f 1  | grep -v dev

インストールされているパッケージ

liblua5.1-0
liblua5.2-0
liblua5.3-0
libluajit2-5.1-2
libluajit2-5.1-common
libnginx-mod-http-lua
lua-ansicolors
lua-argparse
lua-basexx
lua-binaryheap
lua-bit32
lua-bitop
lua-busted
lua-cgi
lua-cjson
lua-cliargs
lua-compat53
lua-copas
lua-cosmo
lua-coxpcall
lua-cqueues
lua-curl
lua-curses
lua-cyrussasl
lua-dbi-common
lua-dbi-mysql
lua-dbi-postgresql
lua-dbi-sqlite3
lua-discount
lua-dkjson
lua-event
lua-expat
lua-fifo
lua-filesystem
lua-geoip
lua-hamlib
lua-http
lua-iconv
lua-inifile
lua-inotify
lua-inspect
lua-json
lua-ldap
lua-lgi
lua-logging
lua-lpeg-patterns
lua-lpeg
lua-lpty
lua-luaossl
lua-luassert
lua-luv
lua-md5
lua-mediator
lua-messagepack
lua-mmdb
lua-mpack
lua-nvim
lua-penlight
lua-posix
lua-readline
lua-redis
lua-resty-core
lua-resty-lrucache
lua-rex-gnu
lua-rex-onig
lua-rex-pcre2
lua-rex-posix
lua-rex-tre
lua-rings
lua-rrd
lua-say
lua-sec
lua-socket
lua-sql-mysql
lua-sql-odbc
lua-sql-postgres
lua-sql-sqlite3
lua-system
lua-systemd
lua-term
lua-unbound
lua-unit
lua-uri
lua-wsapi-fcgi
lua-wsapi
lua-yaml
lua-zlib
lua5.1
lua5.2-doc
lua5.2

nvim 関連で結構いろいろはいっていたっぽい

ディレクトリがマウントされているか調べる

いまのマウントポイントは、proc ファイルシステムから取得できる。

cat /proc/mounts 

これをgrep すればマウント済みか判定できる

cat /proc/mounts  | grep dir 

また、grep には -qs で結果の true/false だけを取れるので。判定に使える。

ディレクトリ(PATH)がマウントされているか調べる。

dir=/path/to/check
grep -qs $dir /proc/mounts && echo $dir is  mounted 

コレを使うと、シェルスクリプトで便利になる。

dir=/path/to/check
if grep -qs $dir /proc/mounts ; do 
  echo $dir is  mounted 
else 
  mount $dir
if

curl でsocks プロキシを使ってssh先ネットワークから外部接続

curl でsocks プロキシを使って別ネットワークから外に出る。

curl でsocks プロキシを使う。

curl --socks4 127.0.0.1:1080 -v  g.co

socks オプションを使えば、HTTPプロキシではないにしろ、簡単にプロキシを挟める。

socks プロキシ作成(SSH

SOCKSプロ棋士の作成は ssh で瞬殺

 ssh -D 1080 remote-router.lan

これで、127.0.0.1:1080 にSOCKSプロキシが立ち上がるんで、あとはcurl で接続すればおっけ

ssh が通ればプロキシ経由できる。

SSHさえ通れば、プロキシを瞬間で作れてネットーワークを繋げれて外に出られる。

正規表現の\K で1つだけなら後方参照を省略できて便利

\K を使うと、そこまでのマッチ部を省略できる

ip -br a からIPv4 を取り出す例

$ ip -br a | grep eth2
eth2             169.254.70.21/16 fe80::4154:6db4:aec3:32f/64

コレを、\k で書くとこうなる。

ip -br a | grep -oP 'eth2\s+\K[\d.]+'

つまり、後方参照を使わなくても、「前読み」で戦えるわけだ。

php の例

これをphpで使うと。次の用になる。

before( グループ )

<?php 

preg_match_all( '/^eth2\s+([\d.]+)/m', `ip -br a ` , $m);
if ( sizeof ($m) > 0 ){
  var_dump($m[1][0]);
}

after ( \K 利用時 )

<?php
preg_match_all( '/^eth2\s+\K[\d.]+/m', `ip -br a ` , $m);

var_dump($m);

if 文が省略できて便利。

ruby とかでも

#!/usr/bin/env ruby
p `ip -br a `.match(/^eth2\s+([\d.]+)/)
p `ip -br a `.match(/^eth2\s+\K[\d.]+/)

結果はこの様になる。

#<MatchData "eth2             169.254.70.21" 1:"169.254.70.21">
#<MatchData "169.254.70.21">

if文などチェックが簡潔になる。

ネストが減らせて便利。

知ってたら楽、知らなくていいコト。そして、初見殺しである。知らない人にはなぜそうなるか全く想像がつかない間違ったソースコードに見える可能性があって怖いけど便利。

関連資料

Grep に'\K' という便利な書式がある。 - それマグで!

広告フィルタ・ルールを統合して圧縮できる。

ああ、そうかAdguardのルールって1行1つじゃなくてもいいのか。

Adguardのブロックルール

example.tld##.class_a
example.tld##.class_b

だとすると

example.tld##.class_a, class_b

と書いても同じなのね。

これで行数を節約したり、記述を統合して場所ごとに管理しやすくできるわ。

迷惑広告が増えていてルールが増えていく

ad filter (広告フィルタ)の行数を節約したい。

iOSには上限がある。行数が多いと登録・更新に時間がかかる。ロード後のフィルタ適用が遅くなり、ページロード高速化のためにやってるブロックが読み込み速度を遅延させるという本末転倒が起きる。

adguard など iOSのフィルタルールは行数に制約があります。

iOSのフィルタルールにはバグが有り、本来の上限より、ずっと低い値しか入らないバグがあったりする。

https://adguard.com/ja/blog/adguard-v4-5-1-for-ios.html

行数の節約を考えて、ルールを見直していて気づいた。

行数を節約したり、あれこれ考えていて、「1行で複数にマッチする」ように書けばいいことに気づいた。

例えば、xpath

:xpath( /a or /b )

例えば、CSS

##.class_a, .class_b

うまくやれば、管理が楽になりそう。

LUKSなディスクを追加するコマンド

luks のディスクを作る

LUKSのディスクを作るときに、鍵ファイルや、スペア鍵、パスフレーズを設定するのがめんどうだったの手順をできる限り、簡素化して、スクリプトにまとめた

## デバイスを措定
DEV=/dev/sde  

## 鍵ファイルを用意
SPARE=/boot/keys/spare.keybin
KEY=/boot/keys/disk.keybin 
SECTOR=512
## パスフレーズを用意
PASS=$(cat /enc/pass |  openssl enc -d  -pbkdf2 -base64 -aes256)

## 鍵を3つ登録
##  --sector-size 注意する
sudo cryptsetup luksFormat --key-file=$KEY --sector-size=$SECTOR $DEV
sudo cryptsetup luksAddKey --key-file=$KEY --new-keyfile=$SPARE $DEV
sudo cryptsetup luksAddKey --key-file=$KEY $DEV < <(echo -n $PASS)

## テスト
sudo cryptsetup luksOpen --test-passphrase $DEV < <(echo -n "$PASS")
sudo cryptsetup luksOpen --test-passphrase --key-file=$KEY $DEV
sudo cryptsetup luksOpen --test-passphrase --key-file=$SPARE $DEV

## luks のIDを表示
sudo cryptsetup luksUUID $DEV

# Extract the model number and serial number using grep
hdparm_output=$(sudo hdparm -I "$DEV" )
model_number=$(echo "$hdparm_output" | grep -oP "Model Number: \K.*" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
serial_number=$(echo "$hdparm_output" | grep -oP "Serial Number: \K.*" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
echo $model_number
echo $serial_number

## crypttab に登録
MAP_NAME=crypt-disk10

printf "%s UUID=%s %s luks,keyslot=0 # %s/%s %s \n" \
 $MAP_NAME $(sudo cryptsetup luksUUID $DEV) $KEY "$model_number" "$serial_number" $(date -I)

/etc/crypttab に書いたらテスト

暗号ディスクを展開する。

sudo cryptdisks_start crypt-disk10        
sudo cryptdisks_stop crypt-disk10        
sudo cryptdisks_start crypt-disk10        

既存のディスクを入れ替える。

既存のLVMに、組み込んで

sudo pvcreate /dev/mapper/crypt-disk10      
sudo vgextend data /dev/mapper/crypt-disk10   

既存のLVMのデータを移動させる

sudo pvmove /dev/mapper/crypt-disk05 /dev/mapper/crypt-disk10      
sudo vgreduce data /dev/mapper/crypt-disk05
sudo pvremove /dev/mapper/crypt-disk05
## もし、容量拡大ディスクなら resizeも必要。容量縮小なら、shrink する。
sudo resize2fs /dev/mapper/NAME

数TBあると時間かかるので、あとはのんびりと。

過去記事に書いた内容を整理整頓した。

vs code が右クリックメニューをスパム行為するので消す。

vs code が右クリックメニューが嫌い。

  • Drive
  • Directory
  • Background
  • all file(*)

これらにメニューをスパム行為のように登録してくる。とても使いにくい。

レジストリを操作する。

UAC昇格した、PowerShellで、次のコマンドを実行する。

## VS Code がうるさいので消す。
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Classes\Drive\shell\VSCode' -Name 'LegacyDisable' -PropertyType 'String' -Value ''
Move-Item -LiteralPath 'HKLM:\SOFTWARE\Classes\Drive\shell\VSCode'  -Destination 'HKLM:\SOFTWARE\Classes\Drive\shellex\VSCode'

New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Classes\Directory\shell\VSCode' -Name 'LegacyDisable' -PropertyType 'String' -Value ''
Move-Item -LiteralPath 'HKLM:\SOFTWARE\Classes\Directory\shell\VSCode'  -Destination 'HKLM:\SOFTWARE\Classes\Directory\shellex\VSCode'

Move-Item -LiteralPath 'HKLM:\SOFTWARE\Classes\*\shell\VSCode'  -Destination 'HKLM:\SOFTWARE\Classes\*\shellex\VSCode'

きれいに消える。Shift押したときのEXメニューにはちゃんと残るので良い。

完全に無効化する場合

次の方法で、LegacyDisableという、空文字のエントリを作成する。

$path='HKLM:\SOFTWARE\Classes\Drive\shell\VSCode'
New-ItemProperty -LiteralPath $path  -Name 'LegacyDisable' -PropertyType 'String' -Value ''

直接削除するか、別名に変更でも構わないが、LegacyDisable をつかって無効化しておくのが無難だと思う。

shell ex ( shift 押したときのメニュー)に変える場合

shellshellex に変えればいい 。

$src='HKLM:\SOFTWARE\Classes\Drive\shell\VSCode'
$dst='HKLM:\SOFTWARE\Classes\Drive\shellex\VSCode'
Move-Item -LiteralPath $src  -Destination $dst

これらの方法は、ほかの右クリックメニューを排除するけど、たまに使うってときに便利です。

git-bash が右クリックメニューを占拠するので消す。

vscode と git-bash は現代では基本ツールだと思うけど。

どうしてこんなに右クリックメニューを専有するのだろうか。

git-bash は、vscode から使うだろうし、そこまでメニュー統合してほしいとは思わない。

git-bash をコンテキスト・メニュー(右クリックメニュー)から消す。

## git-bash を消す。exへ移動する。
Move-Item -LiteralPath 'HKLM:\SOFTWARE\Classes\Directory\shell\git_gui' -Destination 'HKLM:\SOFTWARE\Classes\Directory\shellex\git_gui' 
Move-Item -LiteralPath 'HKLM:\SOFTWARE\Classes\Directory\shell\git_shell' -Destination 'HKLM:\SOFTWARE\Classes\Directory\shellex\git_shell' 

New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Classes\Directory\background\shell\git_gui'   -Name 'LegacyDisable' -PropertyType 'String' -Value ''
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Classes\Directory\background\shell\git_shell' -Name 'LegacyDisable' -PropertyType 'String' -Value ''

windows がv6/v4 でping に応答しない。

windows はdefaultでping応答しない。

ファイア・ウォール設定で icmp echo の igress (inbound) を有効にする必要がある。

応答(icmp reply) については出ていくパケットに制限が掛かってないので特に設定の必要がなかった。

Core Networking の項目の中にあるので、探すのはちょっとめんどくさい。

v4もv6 もicmp echo を切られているので、ちょっとめんどくさい。

コマンドから

v4 の icmp echo を有効にする。

netsh advfirewall firewall add rule name="ICMP Allow incoming V4 echo request" protocol=icmpv4:8,any dir=in action=allow

icmp はオフにするものなの?

icmp echo をデフォルトでOFFにする理由ってなんなのでしょうか。百台規模でLAN組んでる組織なら自分で設定するべきだろうし、一般ユーザ向けOSでこの設定の意味がわからん。巨大なLANだとしてもスイッチやGWで切ってしまえばいいようなものだと思うんですが。

ping 応答しないのがセキュリティなんですかねぇ。icmp 応答拒否はデメリットも多いと思うんです。巨大なLANならともかく、小さいLANでping(icmpv4)を拒否しても、ARPは飛び交ってるわけで。ping拒否はセキュリティなんですかね。

Windowsは詳しくないけど、ping は 応答しないのが世間の常識らしい。従っておくしかないようです。