如何使用 bash 終端捕獲表達式?

如何使用 bash 終端捕獲表達式?

我有共享相同命名法的檔案log_<date>.txt分散在嵌套目錄中root。其中<date>是以 YYYYmmddHHMM 格式建立檔案的日期。對於程式碼相容性問題,我想將所有檔案重命名為<date>_log.txt

我知道我必須捕獲該<date>表達式,但如何在 bash 中做到這一點?另外,如何在嵌套目錄中執行此操作?

答案1

假設您在 shell 變數中有這些檔案之一的名稱namelog_然後使用 完成從變數值中刪除該位${name#log_},並.txt使用 完成刪除該位${name%.txt}。剩下的就是日期,而它的格式本質上並不有趣。可以輕鬆獲取剝離的值並將其添加_log_.txt到其末尾以創建新名稱。

若要對單一目錄中的檔案執行此操作,請使用循環。請注意,我們現在也必須刪除變數值開頭的目錄名稱。

shopt -s nullglob

for name in root/log_*.txt; do
    newname=root/${name#root/log_}    # remove prefix, add root/ back
    newname=${newname%.txt}_log.txt   # remove suffix, add _log.txt back

    printf 'Would move "%s" to "%s"\n' "$name" "$newname"
    # mv -i "$name" "$newname"
done

mv為了安全起見,我已經註解掉了實際的命令。您應該測試運行一次,看看首先會發生什麼。

shellnullglob選項使bashshell消除不匹配的模式而不是讓它們不擴展。這意味著如果log_*.txt給定目錄中沒有文件,則循環根本不會運行。

這可以遞歸地應用於當前目錄(包括當前目錄)下的任何此類文件或多或少只需修改我們循環的模式:

shopt -s globstar nullglob

for name in ./**/log_*.txt; do
    dirpath=${name%/*}                      # get path of directory
    newname=$dirpath/${name#$dirpath/log_}  # remove prefix, add directory path back
    newname=${newname%.txt}_log.txt         # remove suffix, add _log.txt back

    printf 'Would move "%s" to "%s"\n' "$name" "$newname"
    # mv -i "$name" "$newname"
done

其他變更包括啟用globstarshell 選項,該選項允許我們用於**匹配子目錄。另外,我還選擇了目錄路徑,以便能夠將其新增至新名稱之前。

您也可以用於find產生循環的輸入:

find . -type f -name 'log_*.txt' -exec sh -c '
    for name do
        dirpath=${name%/*}                      # get path of directory
        newname=$dirpath/${name#$dirpath/log_}  # remove prefix, add directory path back
        newname=${newname%.txt}_log.txt         # remove suffix, add _log.txt back

        printf "Would move \"%s\" to \"%s\"\n" "$name" "$newname"
        # mv -i "$name" "$newname"
    done' sh {} +

這將找到當前目錄或以下目錄中名稱匹配的所有常規文件log_*.txt,然後將它們批量傳遞到內聯sh -c腳本,該腳本基本上執行與上面第二個變體中的循環相同的工作。

答案2

最好是使用批次檔重命名工具,例如zsh'szmvmmv的基於 perl 的變體之一rename

zsh << 'EOF'
autoload zmv
zmv 'log_(*).txt' '${1}_log.txt'
EOF

或者

prename 's/log_(.*)\.txt/${1}_log.txt/' log*.txt

相關內容