2024年8月31日 星期六

遠端桌面 無法使用 Microsoft 帳戶登入

遠端桌面 無法使用 Microsoft 帳戶登入

這個應該是Bug,提供一個比較萬解的辦法可以更新本地密碼的指令

複製底下的指令

runas /u:YourMail@gmail.com cmd.exe

然後打開終端機貼上並把 YourMail@gmail.com 這個部分改成你自己的帳號
然後正常輸入密碼登入一次,就會順便更新你的密碼了

會造成這個局面有幾種情況

  1. 更改過密碼
  2. 原始是家用板透過市集或序號升級到進階板
  3. 創建用戶的時候不是直接創建用戶,而是先創建離線用戶後再登入線上帳號

似乎只有直接使用微軟帳號創建Windwos用戶才會順便登陸密碼,不然就都會卡住原因不詳應該是bug。


如果是因為改密碼燈不進去,一個辦法是使用本機帳號登入,離線用戶預設是帳密前五碼,以前面的例子來說

YourMail@gmail.com 這個帳號的本機用戶是 yourm,使用 yourm 與舊密碼還是可以登入的。當然如果是3的情況要查一離線帳號是什麼,不會是預設的前五碼。




其他推薦文章

Rdp遠端桌面連線 降低延並遲提高禎數到 60fps



參考

解決 Microsoft 帳戶無法登入遠端桌面 | Then Notes 隨筆

2024年7月31日 星期三

移除 Visual Studio 桌面右鍵選單

移除 Visual Studio 2022 桌面和資料夾的右鍵選單

自行打開終端機執行下面指令

reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\AnyCode" /v "LegacyDisable" /t REG_SZ /d "" /f
reg add "HKEY_CLASSES_ROOT\Directory\shell\AnyCode" /v "LegacyDisable" /t REG_SZ /d "" /f

執行好之後重啟 explorer 就會消失了,不知道怎麼重啟直接重開機也行。



對應的登陸檔長這個樣子

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\Background\shell\AnyCode]
"LegacyDisable"=""

[HKEY_CLASSES_ROOT\Directory\shell\AnyCode]
"LegacyDisable"=""



2024年6月11日 星期二

python click 參數中自訂自己的檢查類 type

python click 參數中自訂自己的檢查類 type

原生函式其實已經非常足夠了,只是如果專案比較刁鑽一點又不想把檢查函式寫在 command 函式中,而是傳入之前檢查可以自訂類來做

目標先挑一個你覺得有用的免得要重寫太多功能,這邊假設是路徑就挑 click.Path 這個類別繼承,然後在此之上加上自己的功能

# 自訂 type 類別 ExtPath 用例
class ExtPath(click.Path):
    def __init__(self, my_check=False, **kwargs):
        self.my_check = my_check
        super().__init__(**kwargs)

    def convert(self, value, param, ctx):
        print(f"ExtPath.convert():: 啟動此函數的對象參數是 {param.name} 目前值為 {value}, 自訂參數 my_check={self.my_check}")
        return super().convert(value, param, ctx)

# 群組命令3:: demo2
@entry.command()
@click.argument('name', required=True, nargs=1, type=ExtPath())
@click.argument('path', required=True, nargs=-1, type=ExtPath(my_check=True, exists=True))
def demo2(name, path):
    cli.echo_inf_message(f"[\n    name: {name}\n    path: {path}\n]")
    """獲取從cli設置的初始化信息"""

大致上會用到的部分都寫上了,自己參考著應該就能知道怎麼使用了


範例用例

(.venv) PS C:\Users\user\OneDrive\Git Repository\PyCli_Template> myapp demo2 Test setup.py *.ps1     
ExtPath.convert():: 啟動此函數的對象參數是 name 目前值為 Test, 自訂參數 my_check=False
ExtPath.convert():: 啟動此函數的對象參數是 path 目前值為 setup.py, 自訂參數 my_check=True
ExtPath.convert():: 啟動此函數的對象參數是 path 目前值為 deploy.ps1, 自訂參數 my_check=True
[
    name: Test
    path: ('setup.py', 'deploy.ps1')
]


需要講的大概就是順序了,這個類的順序在參數擴展之後的 for 迴圈之內,就算是附加在 nargs=-1 裡面也會自動跑多次,可以從範例中看到。

這裡處理完畢之後就會正式進入 comannd 函式了,可以在這裡修改變數值但不是很建議,可能會跟官方功能有衝突。





2024年6月7日 星期五

python 裝飾器 時間計時範例

python 裝飾器 時間計時範例

來自高天的講解中複製出來的代碼
【python】装饰器超详细教学,用尽毕生所学给你解释清楚,以后再也不迷茫了!

import time

def timeit(iteration):

    def inner(f):

        def wrapper(*args, **kwargs):
            start = time.time()
            for _ in range(iteration):
                ret = f(*args, **kwargs)
            print(time.time() - start)
            return ret

        return wrapper

    return inner

@timeit(1000)
def double(x):
    return x * 2

double(2)

這個例子很關鍵的地方在於事後忘記了看一眼就馬上明白了

怎麼理解最快,這個最快的辦法就是不要理解

首先第一個問號區 *args, **kwargs ,只需要記住一件事情這整個區塊叫做 完美轉發 我不管前面的人給我什麼反正我就是直接複製對方的參數設定值

剩下的直接都當作一個區塊記下來就好了,只需要知道從 1000 那個位置可以設置 for 迴圈的變數,然後在 ret 的位置就是執行原本的函式

在 wrapper 中你可以對這個函式的前跟後動手,你希望在函式執行前先弄點什麼就寫在 ret 之前,你希望在執行後再弄點什麼就寫在 ret 之後

而這個對函式前後動手的行為就是裝飾器中的一個核心價值了,只需要透過追加一行就可以包裝函式前後的動作了




如果要放在類裡面參考這篇

【python】如何在class内部定义一个装饰器?这里的坑你要么不知道,要么不会填! (youtube.com)


2024年6月6日 星期四

python click 如何避免參數中的通配符被展開

python click 如何避免參數中的通配符被展開

在這篇討論中有被提到
Expand globs in arguments on Windows? · Issue #1096 · pallets/click (github.com)

click預設會通過 glob 展開通配符這是為了確保在 Windows 或 Linux 上能獲得一致的效果
不過反過來說如果你就是要傳入通配符,其實沒有任何手段可以避免的,原作似乎就沒開放了

討論的最後有提到改進在這裡
expand patterns in Windows args by davidism · Pull Request #1830 · pallets/click (github.com)

那就沒辦法只能硬幹了,就把這代碼幹掉

import click

# 定義一個新的 _expand_args 函數,直接返回原始參數
def _no_expand_args(args):
    return args
# 猴子補丁覆蓋 Click 的 _expand_args 方法
from click import core
core._expand_args = _no_expand_args

@click.command()
@click.argument('path', type=str)
def process_path(path):
    click.echo(f"Received path: {path}")

if __name__ == '__main__':
    process_path()


這樣一來就不會被騷擾了,反過來說你也可以在這裡寫上屬於自己的條件

上面是簡寫完整一點的整個函式其實是長這樣的

# 定義一個新的 _expand_args 函數,直接返回原始參數
import typing as t
def _no_expand_args(
    args: t.Iterable[str],
    *,
    user: bool = True,
    env: bool = True,
    glob_recursive: bool = True,
) -> t.List[str]:
    return list(args)
# 猴子補丁覆蓋 Click 的 _expand_args 方法
from click import core
core._expand_args = _no_expand_args


原始代碼可以在這裡看,可以參考是如何用 glob 展開變數的
click/src/click/utils.py at 923d197b56caa9ffea21edeef5baf1816585b099 · pallets/click (github.com)



補充說明一下流程,我覺得 click 原作這個解不夠漂亮,但確實已經是沒有辦法中的一個還算可以的辦法了。

原作的做法是收到來自命令的參數時,就先進行展開,不管是 option 還是 augument 都會被展開,這個有個問題是

在 linux 上 bash 會直接展開後才輸入,所以實際 click 拿到的參數已經是展開後的,看起來很美好的統一了 Win 和 linux,其實背後是犧牲了一個特性

如果想試圖傳遞通配符給程序,通常的做法是在 bash 中輸入雙引號,以便告訴 bash 不要展開這個字串直接輸入到程序中

問題就在這裡,click拿到參數之後不管如何總之每個都先過一輪 glob() 就對了,所以最終還是會被展開了,儘管已經用雙引號

好處就是統一了 Windwos 跟 Linux 的邏輯,背後是犧牲透過雙引號輸入通配符號的可能性, Click 強制 glob() 導致無論如何參數都會被展開




不過其實有邏輯漏洞可以卡,透過這個方式 `myapp -p"*.py"` 或是全寫也行,這樣過 glob() 的時時候由於 `-p"*.py"` 不是路徑就不會被展開了

如果 -p 後面有空格,那就會被切開來分兩次進glob() 還是會被展開,類似技巧還有別的組合可以實現


繼續回來原本的話題,這個要比較合理的解決,估計還是只能開放 glob 的自由度吧,預設不要開,自己決定要不要展開,而且最好還是可以決定系統層級的 `glob_win=Ture`, `glob_unix=Ture`。

這樣至少 linux 上還可以保持透過雙引號控制要不要展開,而Windwos這沒辦法了終端機本來就不會展開了,不管雙不雙引號都一樣。

兩邊系統的統一性我覺得不是那個重要,對於 win 本來就要知道系統不會幫你展開,對於 unix 本來就要知道系統會擅自展開。

函式只需要提供一個快捷讓你自己決定要不要賭這個洞。





PowerShell 通配符 Get-Item 無法帶有方括號名稱的檔案 [*]

PowerShell 通配符 Get-Item 無法帶有方括號名稱的檔案

這算是 PowerShell 5.1 預設給的方便的,不過在不知情的情況下就造成bug了

假設有一個 [1]File.txt 的檔案,於是就這樣獲取

Get-Item [1]File.txt

居然什麼都沒有,於是乎聰明如你,用引號總能解吧?

Get-Item "[1]File.txt"
Get-Item '[1]File.txt'


想不到吧很遺憾還是沒有辦法

這東西叫做通配符,內建在 Get-Item 裡的所才沒法通過引號處理的
about Wildcards - PowerShell | Microsoft Learn



情況1

如果你只是單純想解決括號問題,沒有要搞通配符號組合,參考這個就能解了。

# 使用函式接口 LiteralPath (就是拒用萬用符拉)
Get-Item -LiteralPath 'Test[1]'.txt

# 連帶反轉意符號一起傳進去
Get-Item '.\Test`[1`].txt'

# 如果用雙引號圍住的話傳入前就會被解掉要多2個反引號
Get-Item ".\Test```[1```].txt"





情況2

另一個坑是當括號與星號一起出現時,又會導致另一個解析問題,括號被當作通佩服一起解釋了,就是你想匹配 Test[*].txt 多個檔案的時候。

這時候情況更複雜了一些,你得連反引號都一起傳進去才能,免得反引號在傳入的當下被解析了少了一次解析。

# 連帶反轉意符號一起傳進去
Get-Item '.\Test``[*``].txt'

# 雙引號的話更精彩一點,堪稱這在衝三小
Get-Item ".\Test`````[*`````].txt"


測試結果




情況3

2024-09-28 發現一個新問題 'Test``[*``].txt' 實際上匹配的並不是 Test[*].txt 而是 Test*.txt,說起來繞口,原因是那個方括號被當作萬用字解讀了。

如何更明白的理解問題可以看下面這個例子

# 匹配檔名為 Test[0].txt, Test[1].txt 的檔案
Get-Item '.\Test``[[0-1]``].txt'



這樣寫出來應該就很好理解了,因為萬用字的匹配是在 cmdlet 裡面做的,所以才導致這個反人類思維的解...

第一層反引號會在傳入的時候被解掉,此時算法實際吃到的是一個反引號的 `[ 所以她成功識別是一個引號,第二個由於就是引號沒什麼好說的吃到引號當作萬用字解讀。

也就是可以理解成 [0-1] 這東西會被當作一個特殊字串對待,剩下的保持原本模樣。

那為什麼 '.\Test`[1`].txt' 能夠被正確識別呢? 因為 [1] 沒有構成合法的萬用字元所以被當作字串解讀了 (這鬼邏輯...雖然是正確的但別這樣設計搞人啊)


情況4

以為這樣就結束了嗎? 不還有一個更鬼畜,記得先溫得好情況3的神奇邏輯,這是同一套邏輯的變態版,現在考慮到檔名含有雙引號的情況

# 匹配檔名為 Test`[0].txt, Test`[1].txt 的檔案
Get-Item '.\Test``````[[0-1]``].txt'



居然是要添加4個引號? 是的這個邏輯的是正確,因為傳入的時候會被吃掉一次只剩2個,真正在運算的時候又被吃掉一次只剩1個,所以正確的識別了

這個鬼邏輯來自於,不知道為啥路徑相關的cmdlet會對路徑做一次多餘的雙引號解析導致的,啥原因設計成這樣就不知道了。



下面是測試的方法,看了應該就秒懂上面的 情況4 發生什麼事情了
 (注意雙引號與單引號)

# 模擬 Get-Item 是如何處理萬用字元的
'.\Test`[1].txt' -like ".\Test``````[[0-1]``].txt"

對他們是相等的...也就是說對於所有路徑相關的 Get-Item, Test-Path 等等必須自己有自覺的認識到,即使我用了單引號避免被解釋,但實務上進去還會被解析一次。


這個真該打屁股了誰寫的反人類設計...

不過嘴砲歸嘴砲,八成是身處歷史當下的那群人,遇到某個無法解決的問題,提出的無可奈何的解吧...。現在好拉已經過這麼久了,想修估計也改不了了,成為萬世毒瘤了


對於這個局面的處置方法

對於這個局面要人性化的處置可以參考這下面這個解法

# 匹配的真實路徑 Test`[1].txt
Test-Path ('Test```[[0-1]`].txt' -replace('`','``'))
Test-Path ('Test```[*`].txt' -replace('`','``'))


這少這樣理解度就高一點了,為什麼會有三個是因為需要反轉意一個反引號跟方括號導致的,是可以讀懂的代碼。




深挖bug的本質

2024-10-09

這就完事了嗎? 並沒有,我又再次深入地去挖掘這個問題,發現老早就一堆人在講了

  1. 反引號轉義不一致 ·問題 #7999 ·PowerShell/PowerShell (github.com)
  2. 工作目錄路徑與相對路徑中的通配符 ·問題 #24260 ·PowerShell/PowerShell (github.com)


但是呢六年過去還是沒修,八成是因為歷史包袱改不了了。最大的絆腳石應該是因為

永遠都無法區分路徑字串上的 D:\test\Test`[0-2] 反引號到底是跳脫字元還是普通字符。


回到剛剛的話題為什麼這事情還沒完是因為,我們已經知道雙重跳脫可以協助我們區分到底誰是字元誰是跳脫

D:\test\Test```[0-2`]

我們上個色就知道誰是跳脫了,看上去很美好對吧,但是並沒有 PowerShell 在遇到反引號加上一個任意字符,再加上一個右方括號的時候,bug就由此而生

我找了很久還是沒找到這到底對應到哪個路徑,最後才發現

D:\test\Test```[0-2`] 可以找到 D:\test\Test`[0-2] 這個帶反引號的資料夾,但是但是

D:\test\Test```[0-2`]\File.txt 不能找到 D:\test\Test`[0-2]\File.txt 的檔案

很神奇吧,所謂的雙重引號只有最右邊的斜線有效而已,如果是中間層級的資料夾,雙重反引號是無效的。


好在社群有人發現另一個有趣的現象是,-include 的邏輯跟 -like 一樣是沒有雙重反引號的,我們可以鑽這個空子

function fix_get_item($Path){
    $dir  = (Split-Path $Path) -replace('`','``')
    $file = (Split-Path $Path -Leaf)
    $type = (Get-Item -Path $dir).PSProvider.Name
    (Get-ChildItem $dir -Recurse -Depth 1 -Include $file).FullName
} # fix_get_item 'D:\test\Test```[0-2`]\README`[[0-2]`].md'

利用 Get-ChildItem 去帶的好處是

  1. for迴圈交由 Get-ChildItem 處理絕對比自己正則快很多很多
  2. 不需要去理會邏輯部分,交由現成的函式去處理
  3. 現在父資料跑到最右邊,父資料夾也支持奇耙路徑了 (反引號-任意字符-右方括號)


不過問題淺顯易見,爺資料夾以上還是只能使用正常路徑+通配符,但這問題不大已經足夠覆蓋絕大多數的情況了。





參考

  1. Wildcard matching when filename has square brackets · PowerShell/PowerShell · Discussion #18146 (github.com)



2024年5月29日 星期三

Linux 中仿 tree 輸出的 Shell 檔案

Linux 中仿 tree 輸出的 Shell 檔案

2024-05-29 一個比較完整的檔案寫完了放在gist上 Tree.sh (github.com)




某些環境基於某些原因實在是沒辦法,只能自己自幹一個這邊給一個 GPT 的作品,最基本基本的功能是有了


tree.sh

#!/bin/bash

# Function to print the folder name with color
function print_folder() {
    local folder_name=$1
    local COLOR_RESET="\033[0m"
    local COLOR_FOLDER="\033[1;34m" # Blue color for folders
    echo -e "${COLOR_FOLDER}${folder_name}/${COLOR_RESET}"
}

# Function to recursively print the directory tree structure
function print_tree() {
    local directory=$1
    local prefix=$2
    local files=("$directory"/*)
    local last_index=$((${#files[@]} - 1))

    # Check if the directory is empty
    if [ "$files" = "$directory/*" ]; then
        return
    fi

    for i in "${!files[@]}"; do
        local file="${files[$i]}"
        local basename=$(basename "$file")
        local new_prefix="$prefix"

        if [ $i -eq $last_index ]; then
            echo -n "${prefix}└── "
            new_prefix="$prefix    "
        else
            echo -n "${prefix}├── "
            new_prefix="$prefix│   "
        fi

        if [ -d "$file" ]; then
            print_folder "$basename"
            print_tree "$file" "$new_prefix"
        else
            echo "$basename"
        fi
    done
}

# Check if a directory is provided as an argument
if [ -z "$1" ]; then
    echo "Usage: $0 <directory>"
    exit 1
fi

# Remove trailing slash from the directory if it exists
directory=$(echo "$1" | sed 's:/*$::')

# Print the initial directory with color
print_folder "$(basename "$directory")"
print_tree "$directory" ""


使用的話跟 tree 差不多沒兩樣先給執行權限

chmod +x tree.sh

然後使用

./tree.sh testdir


輸出結果

testdir/
├── file1.txt
├── file2.txt
├── subdir1/
│   ├── file2.txt
│   ├── file3.txt
│   ├── subsubdir1/
│   │   └── file4.txt
│   └── subsubdir2/
│       └── file5.txt
└── subdir2/
    ├── file3.txt
    ├── file6.txt
    └── subsubdir1/
        └── file7.txt




精簡高壓版本

邏輯一樣的只是完全不管可讀性,能壓能壓版本

tree(){
    [ -z "$1" ] && { echo "Usage: $0 <directory>"; exit 1; }
    print_folder() { echo -e "\\033[1;34m${1}/\\033[0m"; }
    print_tree() { local dir=$1 prefix=$2; local files=("$dir"/*)
        print_tree_core() { [ $1 -ge ${#files[@]} ] && return
            local idx=$1; local file="${files[$idx]}"
            local base=$(basename "$file") new_prefix="$prefix"
            ([ $idx -eq $((${#files[@]} - 1)) ]) && {
                echo -n "${prefix}└── "; new_prefix="$prefix    "
            } || { echo -n "${prefix}├── "; new_prefix="$prefix│   "
            }; ([ -d "$file" ] && {
                print_folder "$base"; print_tree "$file" "$new_prefix"
            } || echo "$base"); print_tree_core $(($idx + 1))
        }; [ "$files" = "$dir/*" ] && return || print_tree_core 0
    }; dir=$(echo "$1" | sed 's:/*$::')
    print_folder "$(basename "$dir")"; print_tree "$dir" ""
}

貼上之後直接呼叫 tree 就可以用了 (要是原本就有會被覆蓋功能,被蓋了重啟終端就好)

tree testdir



增加顯示有多少個檔案

tree(){
    [ -z "$1" ] && { echo "Usage: $0 <directory>"; exit 1; }
    print_folder() { echo -e "\\033[1;34m${1}/\\033[0m"; }
    print_tree() { local dir=$1 prefix=$2; local files=("$dir"/*)
        print_tree_core() { [ $1 -ge ${#files[@]} ] && return
            local idx=$1; local file="${files[$idx]}"
            local base=$(basename "$file") new_prefix="$prefix"
            ([ $idx -eq $((${#files[@]} - 1)) ]) && {
                echo -n "${prefix}└── "; new_prefix="$prefix    "
            } || { echo -n "${prefix}├── "; new_prefix="$prefix│   "
            }; [ -d "$file" ] && { dir_cnt=$((dir_cnt + 1))
                print_folder "$base"; print_tree "$file" "$new_prefix"
            } || { echo "$base"; file_cnt=$((file_cnt + 1))
            }; print_tree_core $(($idx + 1))
        }; [ "$files" = "$dir/*" ] && return || print_tree_core 0
    }; dir=$(echo "$1" | sed 's:/*$::')
    print_folder "$(basename "$dir")"; dir_cnt=0; file_cnt=0
    print_tree "$dir"; echo -e "\n$dir_cnt directories, $file_cnt files"
}


增加顯示隱藏檔案

tree(){
    [ -z "$1" ] && { echo "Usage: $0 <directory>"; exit 1; }
    print_folder() { echo -e "\\033[1;34m${1}/\\033[0m"; }
    print_tree() { local dir=$1 prefix=$2; local files
        files=("${dir}"/.* "${dir}"/*); files=(${files[@]/*\*/})
        print_tree_core() { [ $1 -ge ${#files[@]} ] && return
            local idx=$1; local file="${files[$idx]}"
            local base=$(basename "$file") new_prefix="$prefix"
            ([ "$base" == "." ] || [ "$base" == ".." ]) && {
                print_tree_core $(($idx + 1)); return
            }; ([ $idx -eq $((${#files[@]} - 1)) ]) && {
                echo -n "${prefix}└── "; new_prefix="$prefix    "
            } || { echo -n "${prefix}├── "; new_prefix="$prefix│   "
            }; [ -d "$file" ] && { dir_cnt=$((dir_cnt + 1))
                print_folder "$base"; print_tree "$file" "$new_prefix"
            } || { echo "$base"; file_cnt=$((file_cnt + 1))
            }; print_tree_core $(($idx + 1))
        }; [ ${#files[@]} -eq 0 ] && return || print_tree_core 0
    }; dir=$(echo "$1" | sed 's:/*$::')
    print_folder "$(basename "$dir")"; dir_cnt=0; file_cnt=0
    print_tree "$dir"; echo -e "\n$dir_cnt directories, $file_cnt files"
}





魔法咒語

最後是魔法咒語,特地用函式包起來是要把路徑後置到後面,方便貼上直接些改
_(){ d=$(realpath "$1"); echo "$d"; find "$1" | sort | sed '1d;s|^'"$1"'| |;s/\/\([^/]*\)$/|-- \1/;s/\/[^/|]*/|   /g'; };_ .

為什麼要用醜醜的 |-- 而不是更漂亮的 └── 是因為,用 Ascii 以外的符號很容易就噴亂碼,自己根據情況斟酌使用吧