2024年5月18日 星期六

市集中的 python 版本被優先使用,還無法從環境變數中刪除找不到該位置

市集中的 python 版本被優先使用,還無法從環境變數中刪除找不到該位置

Windows中有個隱藏的環境變數,叫做應用程式別名,從市集安裝的似乎預設會從那裏設定,不知道的話真的很頭痛被搞過一次。

位置可以從設定裡面點出來



找到那個元兇關掉就好



Windows11上也大同小異,位置就別找了直接搜尋 "應用程式執行別名" 就好



-


2024年5月17日 星期五

下載 Bats 並安裝到用戶的暫存目錄 (以免安裝的方式啟動)

下載 Bats 並安裝到用戶的暫存目錄 (以免安裝的方式啟動)

完整的流程,從下載Bats到安裝到用戶的暫存目錄,並臨時設置環境變數來使用它。


1. 下載Bats

首先,從GitHub下載Bats v1.11.0的源碼壓縮包:

wget https://github.com/bats-core/bats-core/archive/refs/tags/v1.11.0.tar.gz -O bats-core-1.11.0.tar.gz

解壓縮下載的檔案:

tar -xzf bats-core-1.11.0.tar.gz

進入解壓縮的目錄:

cd bats-core-1.11.0


2. 安裝腳本

原作寫的安裝腳本 bats-core/install.sh 下面是腳本的解析並追加了提示如何添加到環境變數的信息,你也可以直接執行原裝的就好沒有改動。


在解壓縮的目錄中,創建一個名為 install_bats.sh 的文件,並將以下內容粘貼到文件中:-

#!/usr/bin/env bash
set -e

BATS_ROOT="${0%/*}" # 腳本所在目錄的路徑
PREFIX="${1%/}"     # 安裝前綴路徑 (移除路徑結尾的斜線)
LIBDIR="${2:-lib}"  # 庫文件目錄名,默認為 "lib"

# 如果沒有提供安裝路徑,則輸出使用說明到標準錯誤。
if [[ -z "$PREFIX" ]]; then
  printf '%s\n' \
    "usage: $0 <prefix> [base_libdir]" \
    "  e.g. $0 /usr/local" \
    "       $0 /usr/local lib64" >&2
  exit 1
fi

# 使用 install 命令來創建目錄結構並設置適當的權限。
install -d -m 755 "$PREFIX"/{bin,libexec/bats-core,"${LIBDIR}"/bats-core,share/man/man{1,7}}

# 安裝二進制文件、庫和手冊頁。
install -m 755 "$BATS_ROOT/bin"/* "$PREFIX/bin"
install -m 755 "$BATS_ROOT/libexec/bats-core"/* "$PREFIX/libexec/bats-core"
install -m 755 "$BATS_ROOT/lib/bats-core"/* "$PREFIX/${LIBDIR}/bats-core"
install -m 644 "$BATS_ROOT/man/bats.1" "$PREFIX/share/man/man1"
install -m 644 "$BATS_ROOT/man/bats.7" "$PREFIX/share/man/man7"

# 讀取安裝後的 bats 執行文件,修改庫目錄變量後重新寫入。
read -rd '' BATS_EXE_CONTENTS <"$PREFIX/bin/bats" || true
BATS_EXE_CONTENTS=${BATS_EXE_CONTENTS/"BATS_BASE_LIBDIR=lib"/"BATS_BASE_LIBDIR=${LIBDIR}"}
printf "%s" "$BATS_EXE_CONTENTS" > "$PREFIX/bin/bats"

# 輸出安裝完成的訊息
echo "Installed Bats to $PREFIX/bin/bats"

# 提示用戶如何添加 BATS 到環境變數
echo "To temporarily add Bats to your PATH, run:"
echo "export PATH=\"\$PATH:$PREFIX/bin\""


給安裝腳本賦予執行權限:

chmod +x install_bats.sh

執行安裝腳本,將Bats安裝到 ~/tmp/bats 目錄中:

./install_bats.sh ~/tmp/bats


3. 臨時設置環境變數

按照腳本的提示,設置環境變數以臨時使用Bats:

export PATH="~/tmp/bats/bin:$PATH"

這裡的路徑記得要根據安裝的位置輸入,然後這只是臨時的關閉終端機就沒有了


4. 驗證安裝

確認Bats已成功安裝並可用:

bats --version


總結

以上步驟將Bats安裝到用戶的暫存目錄 /tmp/bats,並通過臨時設置環境變數使其可用。這樣可以在不影響系統的情況下使用Bats進行測試。

2024年5月15日 星期三

PowerShell 複製到剪貼簿歷史中

PowerShell 複製到剪貼簿歷史中

預設的 Set-Clipboard 不會顯示在剪貼簿中,這個是他的擴充函式
直接上代碼,自行取用

function Set-ClipboardHistory {
    param(
        [Parameter(Mandatory=$true)]
        [string]$Value,

        [Parameter()] # 記錄在 Windows 的剪貼板歷史
        [switch]$DenyInHistory,

        [Parameter()] # 在用戶的多個設備之間漫遊
        [switch]$Roamable
    )

    # 加載 Windows Runtime 類型
    Add-Type -AssemblyName 'System.Runtime.WindowsRuntime'

    # 創建 DataPackage 和 ClipboardContentOptions 對象
    $dataPackage = New-Object Windows.ApplicationModel.DataTransfer.DataPackage
    $cOptions = New-Object Windows.ApplicationModel.DataTransfer.ClipboardContentOptions

    # 根據參數設定剪貼板內容選項
    $cOptions.IsAllowedInHistory = -not $DenyInHistory
    $cOptions.IsRoamable = $Roamable

    # 設定剪貼板操作為複製
    [int]$RequestedOperationCopy = 1
    $dataPackage.RequestedOperation = $RequestedOperationCopy

    # 使用選項將內容設置到剪貼板
    $dataPackage.SetText($Value)
    [Windows.ApplicationModel.DataTransfer.Clipboard]::SetContentWithOptions($dataPackage, $cOptions) | Out-Null

} # Set-ClipboardHistory "TestString"

-

確保 PowerShell Function 總是返回數組(即便對象是單一物件)

確保 PowerShell Function 總是返回數組(即便對象是單一物件)

這個問題是來自於 PowerShell 在經過函式的時候自動將單一一個的數組把數組拆掉變成一個物件的特性導致的。

要怎麼應付這個自動拆解有兩個方法



同一層級使用 @() 包覆

如果是在同一層級只要使用 @() 打包起來就好了,除了過函數會拆數組的特性之外還有一個是在任何時候總是會拆掉數組中的數組,就是不會讓你包兩層數組的意思,除非這兩層的Count都大於1,不然就會被拆掉只剩一層

利用這個特性就管他是不是數組,總之只要包起來就對了

$array = @((Get-Array))


不用寫判斷式也可以保證他是正確的,如果要寫判斷式的話會長這個樣子

$array = if ((Get-Array) -isnot [array]) { @((Get-Array)) }




函式的返回

函式返回就麻煩了一共套了兩層規則,一個是雙層數組拆解跟返回時拆解所以兩層都要解


對於返回值而言只能用逗號傳遞數組

function MyFunc() {
  return ,$array
}


這個第一次看應該覺得很問號,我給你講個猜想應該就可以馬上記住了。就把他當作加一個空物件組成 Count 大於二的數組 @($null, $array),然後出去的時候那個空物件會被解掉。

雖然不確定實作是不是這樣做的,但總之就是對於函式返回值只有這樣才能解,你用第一種出去還是變成物件。


第二個問題是萬一你那個 $array 不是數組的話逗號的保護模被函式搓破了出去還是變成物件了,這個就把兩層保護組合起來就好了

function MyFunc() {
  return ,@((Get-Array))
}


這樣一來就可以不寫判斷式的保證返回值一定是個數組了




PowerShell 獲取剪貼簿歷史紀錄 (包含pwsh)

PowerShell 獲取剪貼簿歷史紀錄

參考自這篇文章並做了一些改良
Enumerating Windows clipboard history in PowerShell - The Old New Thing (microsoft.com)

由於載入的物件僅有 PowerShell 能使用,這導致在 Pwsh 中無法使用,一個簡單的解法就是直接呼叫 PowerShell 來做事就好了

這邊附上完整的函式

function Get-ClipboardHistory {

    # 定義在 Windows PowerShell 中執行的腳本
    $scriptBlock = {
        # 導入 Windows Runtime 支持
        Add-Type -AssemblyName System.Runtime.WindowsRuntime
        # 尋找 AsTask 泛型方法,用於處理異步操作
        $asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object {
            $_.Name -eq 'AsTask' -and
            $_.GetParameters().Count -eq 1 -and
            $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1'
        })[0]

        # 定義 Await 函數,用於等待 Windows Runtime 的異步操作結果
        function Await($WinRtTask, $ResultType) {
            $asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
            $netTask = $asTask.Invoke($null, @($WinRtTask))
            $netTask.Wait(-1) | Out-Null
            $netTask.Result
        }

        # 加載剪貼板相關的 Windows Runtime 類型
        [Windows.ApplicationModel.DataTransfer.Clipboard, Windows.ApplicationModel.DataTransfer, ContentType=WindowsRuntime] | Out-Null
        # 獲取剪貼板歷史項目的異步操作
        $op = [Windows.ApplicationModel.DataTransfer.Clipboard]::GetHistoryItemsAsync()
        $result = Await ($op) ([Windows.ApplicationModel.DataTransfer.ClipboardHistoryItemsResult])

        # 獲取每個歷史項目的文本內容
        $textops = $result.Items.Content.GetTextAsync()

        # 迭代處理每個歷史項目的文本
        $textObjects = @()
        foreach ($textop in $textops) {
            $textObjects += Await($textop) ([String])
        }

        # 將對向轉換為 JSON 字符串
        $textObjects | ConvertTo-Json

    }

    # 使用 Base64 編碼 PowerShell 腳本,並通過 PowerShell.exe 執行
    $encodedBlock = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($scriptBlock.ToString()))
    ,@((powershell.exe -EncodedCommand $encodedBlock | ConvertFrom-Json))

} # (Get-ClipboardHistory)[0]



對於特殊符號有些顯示不出來變成問號,改成 UTF8 可以正常顯示

[console]::OutputEncoding = [Text.Encoding]::UTF8

這其實不是編碼不匹配的問題單純只是中文 BIG5 的符號沒有 UTF8 齊全而已,根據需求自己實作進去吧。



2024年5月13日 星期一

Box Python SDK 如何設置 proxy

Box Python SDK 如何設置 proxy



方法0 透過環境環境變數設置

只需要從 python 環境中設置環境變數即可

import os
os.environ['http_proxy'] = 'http://Enterprise.co.jp:8080'
os.environ['https_proxy'] = 'https://Enterprise.co.jp:8080'

我也有嘗試著從環境變數設置過無效,下面舊文是我掙扎的痕跡,可能是我這邊環境問題。

但總之最後測試的結果是,BOXSDK不用改直接追加這幾行在開頭就行了。


如果你從這邊設置無效順著我底下舊文直接改SDK絕對不受環境影響的。








如何 Excel 垂直合併儲存格中的文字到第一個

如何 Excel 垂直合併儲存格中的文字到第一個

應該經常遇到說上下文被儲存格分開了,需要合併又只能手動剪下文字,不然直接按合併第二格之後的文字會通通不見。

寫了個腳本來處理這件事件,腳本可以指定到快捷鍵加速操作,平常也不需要貼到目標 xlsx 檔案上只需要儲存在一個屬於自己的 xlsm 上面,當你需要這功能的時候打開這個檔案在背景就可以了。

快捷鍵可能每次都要在目標 xlsx 上重設,不過這幾乎不成問題就是了。



腳本

Sub CombineTextInColumns()
    Dim rng As Range
    Dim cell As Range
    Dim startCell As Range
    Dim combinedText As String
    Dim col As Long
    Dim shouldMerge As Boolean

    ' 控制是否合併儲存格的變數
    shouldMerge = False  ' 如果設為 False 則不合併儲存格,如果設為 True 則合併儲存格

    ' 確認用戶已選擇儲存格
    If Not TypeName(Selection) = "Range" Then
        MsgBox "請選擇儲存格"
        Exit Sub
    End If

    Set rng = Selection
    ' 按列處理選定範圍
    For col = 1 To rng.Columns.Count
        Set startCell = rng.Cells(1, col)
        combinedText = startCell.Value
        ' 合併每一列中的文字到第一個儲存格
        For Each cell In rng.Columns(col).Cells
            If cell.Address <> startCell.Address Then
                If combinedText <> "" And cell.Value <> "" Then
                    combinedText = combinedText & Chr(10) & cell.Value
                ElseIf cell.Value <> "" Then
                    combinedText = cell.Value
                End If
            End If
        Next cell

        ' 更新第一個儲存格的內容
        startCell.Value = combinedText
        startCell.WrapText = True

        ' 清除該列中第一個儲存格以外的其他儲存格內容
        For Each cell In rng.Columns(col).Cells
            If cell.Address <> startCell.Address Then
                cell.ClearContents
            End If
        Next cell

        ' 根據條件合併儲存格
        If shouldMerge Then
            rng.Columns(col).Merge
            rng.Columns(col).VerticalAlignment = xlTop
        End If
    Next col

    ' 自動調整所有選定範圍的行高
    rng.Rows.AutoFit
End Sub






補一個日文註解版本的

Sub CombineTextInColumns()
    Dim rng As Range
    Dim cell As Range
    Dim startCell As Range
    Dim combinedText As String
    Dim col As Long
    Dim shouldMerge As Boolean

    ' セルをマージするかどうかを制御する変数
    shouldMerge = False  ' False の場合はセルをマージしない、 True の場合はセルをマージ

    ' ユーザーがセルを選択していることを確認
    If Not TypeName(Selection) = "Range" Then
        MsgBox "セルを選択してください"
        Exit Sub
    End If

    Set rng = Selection
    ' 選択範囲の各列を処理
    For col = 1 To rng.Columns.Count
        Set startCell = rng.Cells(1, col)
        combinedText = startCell.Value
        ' 各列のセルのテキストを最初のセルに結合
        For Each cell In rng.Columns(col).Cells
            If cell.Address <> startCell.Address Then
                If combinedText <> "" And cell.Value <> "" Then
                    combinedText = combinedText & Chr(10) & cell.Value
                ElseIf cell.Value <> "" Then
                    combinedText = cell.Value
                End If
            End If
        Next cell

        ' 最初のセルの内容を更新
        startCell.Value = combinedText
        startCell.WrapText = True

        ' 最初のセル以外のセルの内容をクリア
        For Each cell In rng.Columns(col).Cells
            If cell.Address <> startCell.Address Then
                cell.ClearContents
            End If
        Next cell

        ' 条件に基づいてセルをマージ
        If shouldMerge Then
            rng.Columns(col).Merge
            rng.Columns(col).VerticalAlignment = xlTop
        End If
    Next col

    ' 選択範囲のすべての行の高さを自動調整
    rng.Rows.AutoFit
End Sub