在 bash 字串切片中使用表達式的輸出

在 bash 字串切片中使用表達式的輸出

我想從未知長度的路徑中提取檔案名稱的子字串。我可以分別完成這兩部分,但我想知道是否有一種方法可以在不使用臨時變數的情況下將兩者結合起來?

INPUT_PATH=/path/to/subfolder/file_17.txt 
# I would like to extract "17", the filname will always be 'file_XX.txt'
# The subfolder name is variable length 

TMP=$(basename ${INPUT_PATH})
FILE_NUMBER=${TMP:5:2}
echo ${FILE_NUMBER} # This works as expected

我嘗試過${$(basename $INPUT_PATH):5:2},但這會產生嚴重的替換錯誤。這樣做有什麼技巧嗎?

答案1

採用不同的方法來解決問題並提供僅使用功能的單行解決方案bash

$ cat demo.sh
#!/bin/bash

INPUT_PATH=/path/to/subfolder/file_17.txt

FILE_NUMBER=${INPUT_PATH:((${#INPUT_PATH} -6)):2}
echo ${FILE_NUMBER}
$

$./demo.sh
17

一種更簡單的方法是從字串末尾開始倒數,即

FILE_NUMBER=${INPUT_PATH: -6:2}

顯然,此解決方案取決於以“##XXXX”結尾的字串變量,其中“##”是感興趣的兩位數字,“XXXX”是字串的最後 4 個字元。

答案2

由於您使用的是 bash,因此可以使用正規表示式匹配:

if [[ $input =~ ([[:digit:]]+)\.txt$ ]]; then
    file_num=${BASH_REMATCH[1]}
fi

答案3

最簡單的方法是使用 FILE_NUMBER 而不是 TMP:

FILE_NUMBER=$(basename ${INPUT_PATH})
FILE_NUMBER=${FILE_NUMBER:5:2}

此外,使用參數擴展比調用基本名稱更快:

FILE_NUMBER=${INPUT_PATH##*/}
FILE_NUMBER=${FILE_NUMBER:5:2}

您可以使用 sed 在一行上完成所有操作,但它速度較慢且可讀性較差:

FILE_NUMBER=$(sed 's|.*/||;s/.....\(..\).*/\1/' <<<"$INPUT_PATH")

答案4

對於那些使用zsh而不是bash

  • 第 6 至 7 個字符尾巴basename路徑的(由 )傳回的部分:

    num=${${a_path:t}[6,7]}
    

    $var:tcsh得到tail)

    您也可以使用最近新增的num=${"$(basename -- "$a_path")":5:2}該運算子來實現相容性。命令替換必須加引號,這樣它就不會產生數組,從而選擇一系列字元而不是一系列數組項。然而,使用命令替換和執行最終會比使用內建運算子效率更低、可靠性更低。ksh93 ${var:offset:length}zsh:5:2basenamezsh:t

  • 第一個數字序列尾巴

    num=${(MS)${a_path:t}##<->}
    

    ${var##pattern}pattern是 ksh 運算符,它刪除與from相符的最長前導字串$var。使用該M標誌,M附加的部分將被返回而不是被剝離,並且S會查找Substring,而不僅僅是在開頭。並<->匹配任何數字序列(<x-y>具有未指定的邊界)。

  • _a後面的數字序列尾巴

    num=${${a_path:t}/(#m)*_(<->)*/$match[1]}
    

    (需要extendedglob;注意如果沒有_digitsin ,它會回到完整的尾部$a_path)。

    或者:

    [[ $a_path:t =~ '_([[:digit:]]+)' ]] && num=$match[1]
    

相關內容