我有共享相同命名法的檔案log_<date>.txt
分散在嵌套目錄中root
。其中<date>
是以 YYYYmmddHHMM 格式建立檔案的日期。對於程式碼相容性問題,我想將所有檔案重命名為<date>_log.txt
我知道我必須捕獲該<date>
表達式,但如何在 bash 中做到這一點?另外,如何在嵌套目錄中執行此操作?
答案1
假設您在 shell 變數中有這些檔案之一的名稱name
。log_
然後使用 完成從變數值中刪除該位${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
選項使bash
shell消除不匹配的模式而不是讓它們不擴展。這意味著如果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
其他變更包括啟用globstar
shell 選項,該選項允許我們用於**
匹配子目錄。另外,我還選擇了目錄路徑,以便能夠將其新增至新名稱之前。
您也可以用於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
'szmv
或mmv
的基於 perl 的變體之一rename
:
zsh << 'EOF'
autoload zmv
zmv 'log_(*).txt' '${1}_log.txt'
EOF
或者
prename 's/log_(.*)\.txt/${1}_log.txt/' log*.txt