我完成了这个相对简单的任务,但未能获得最佳结果。
场景:
我有以下文件和文件夹(目录)结构:
.
├── nested-test-folder-1
│ ├── nested\ test\ file\ 1.txt
│ ├── nested\ test\ file\ 2\ tag1.txt
│ ├── nested\ test\ file\ 3\ tag3.txt
│ └── nested\ test\ file\ 4\ tag2.txt
├── nested-test-folder-2-tag1
│ ├── nested\ test\ file\ 5.txt
│ ├── nested\ test\ file\ 6\ tag1.txt
│ ├── nested\ test\ file\ 7\ tag3.txt
│ └── nested\ test\ file\ 8\ tag2.txt
├── nested-test-folder-3\ tag3
│ ├── nested\ test\ file\ 10\ tag1.txt
│ ├── nested\ test\ file\ 11\ tag3.txt
│ ├── nested\ test\ file\ 12\ tag2.txt
│ └── nested\ test\ file\ 9.txt
├── test\ file\ 13.txt
├── test\ file\ 14\ tag1.txt
├── test\ file\ 15\ tag3.txt
└── test\ file\ 16\ tag2.txt
我正在尝试编写一个递归 bash 脚本来相应地重命名文件和文件夹:
- 如果文件/文件夹包含“tag1”或“tag2”,请将它们替换为新的“tag3”。
- 如果文件/文件夹不包含“tag1”或“tag2”且尚未包含“tag3”,则将“tag3”添加到其名称中。
-“tag3”必须添加在文件/文件夹名称的末尾,如果是文件,则必须添加在文件扩展名之前。
- 为了示例,所有文件扩展名都是“.txt”,但在实际情况下它们可以是任何内容,因此需要考虑到这一点。
- 脚本必须忽略隐藏文件和文件夹(. 和 .. 等)、点文件(.zshrc 等)。
此文件的历史记录可在我的 Git 存储库中找到:
https://github.com/atkuzmanov/scripts/tree/master/rename_files_and_folders_script
总结一下我到目前为止所尝试的:
尝试 1 - 一次完成所有操作:
#!/bin/bash
set -e
declare -a STRINGS_TO_REPLACE
STRINGS_TO_REPLACE=("tag1" "tag2")
STRING_TO_ADD_IF_NOT_PRESENT="tag3"
rename_files_and_folders() {
while IFS= read -r -d '' n; do
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$n" == *"$current_string"* ]] || ! [[ "$n" =~ .*"$STRING_TO_ADD_IF_NOT_PRESENT".* ]];
then
rename -d "$current_string" "$n"
break;
fi
done
# echo "$n"
done < <(find . \( -depth -type d -path '*/\.*' -prune -o -not -name '.*' \) -print0)
}
rename_files_and_folders
我得到的输出让我相信它首先重命名一个目录,然后尝试使用该目录的旧名称重命名里面的文件,所以我尝试将文件和目录的重命名拆分为单独的函数。
我还认为首先从所有文件和文件夹中删除“tag1”和“tag2”,然后到处添加“tag3”会更容易。
尝试 2 - 尝试分别处理文件和文件夹(目录):
#!/bin/bash
set -e
declare -a STRINGS_TO_REPLACE
STRINGS_TO_REPLACE=("tag1" "tag2")
STRING_TO_ADD_IF_NOT_PRESENT="tag3"
rename_files_and_folders() {
while IFS= read -r -d '' n; do
# filename="${n##*/}"
# currentfile="${n#.}"
# if [[ -f "$n" ]] && [[ ! -d "$n" ]];
# then
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$n" == *"$current_string"* ]];
then
echo "Will rename $n"
test -e "$n" &&
rename -d "$current_string" "$n"
break;
fi
done
# fi
done < <(find . \( -type f -name "[!.]*" \) -print0)
}
rename_files_and_folders
rename_folders_and_dirs() {
while IFS= read -r -d '' n; do
if [[ -d "$n" ]];
then
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$n" == *"$current_string"* ]];
then
echo "Will rename $n"
test -e "$n" &&
rename -d "$current_string" "$n"
break;
fi
done
fi
done < <(find . \( -depth -name "[!.]*" \) -print0)
}
rename_folders_and_dirs
运行“Try 2”版本时,我得到以下输出:
Will rename ./test-data-files-and-folders-1/nested-test-folder-1/nested test file 2 tag1.txt
Will rename ./test-data-files-and-folders-1/nested-test-folder-1/nested test file 4 tag2.txt
Will rename ./test-data-files-and-folders-1/test file 14 tag1.txt
Will rename ./test-data-files-and-folders-1/nested-test-folder-2-tag1/nested test file 8 tag2.txt
Can't rename './test-data-files-and-folders-1/nested-test-folder-2-tag1/nested test file 8 tag2.txt' to './test-data-files-and-folders-1/nested-test-folder-2-/nested test file 8 tag2.txt': No such file or directory
Will rename ./test-data-files-and-folders-1/nested-test-folder-2-tag1/nested test file 5.txt
Can't rename './test-data-files-and-folders-1/nested-test-folder-2-tag1/nested test file 5.txt' to './test-data-files-and-folders-1/nested-test-folder-2-/nested test file 5.txt': No such file or directory
Will rename ./test-data-files-and-folders-1/nested-test-folder-2-tag1/nested test file 6 tag1.txt
Can't rename './test-data-files-and-folders-1/nested-test-folder-2-tag1/nested test file 6 tag1.txt' to './test-data-files-and-folders-1/nested-test-folder-2-/nested test file 6 tag1.txt': No such file or directory
Will rename ./test-data-files-and-folders-1/nested-test-folder-2-tag1/nested test file 7 tag3.txt
Can't rename './test-data-files-and-folders-1/nested-test-folder-2-tag1/nested test file 7 tag3.txt' to './test-data-files-and-folders-1/nested-test-folder-2-/nested test file 7 tag3.txt': No such file or directory
Will rename ./test-data-files-and-folders-1/test file 16 tag2.txt
Will rename ./test-data-files-and-folders-1/nested-test-folder-3 tag3/nested test file 12 tag2.txt
Will rename ./test-data-files-and-folders-1/nested-test-folder-3 tag3/nested test file 10 tag1.txt
对我来说,它似乎首先重命名一个目录,然后尝试使用旧引用重命名里面的文件,我似乎无法解决这个问题。此外,
find
中的功能中的过滤器 - 一个仅用于文件,一个仅用于文件夹,由于某种原因似乎不起作用。
我尝试过的其他点滴:
## getting just the name of the file
filename="${n##*/}"
## removing the preceding dot
currentfile="${n#.}"
## filtering for just files and not dirs and vice versa
if [[ -f "$n" ]] && [[ ! -d "$n" ]];
then
# ...
fi
接下来我可以尝试什么?
这是我迄今为止提出的一个可行的解决方案,需要优化和清理,它是基本的,我可以使用优化技巧:
#!/bin/bash
set -e
################################
## WORKING
##
## TODO: optimise
##
## WIP: [optimisation 1:]
## Instead of each function looping and going through all files and folders
## have one function to do the loop and pass the files and folders as arguments
## to all the other functions.
## Continue developing function `rename_files_and_folders_dirs_1`.
##
## [optimisation 2:]
## Expand features to take user input.
## For example call the script with flags/arguments/parameters/options
## which invoke different functions passing different arguments
## such as tags to remove or tags to add.
## Note: This could likely make [optimisation 1:] redundant, so need to choose
## witch path to follow.
##
## References
## https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
## https://stackoverflow.com/questions/15012631/rename-files-and-directories-recursively-under-ubuntu-bash
## https://superuser.com/questions/213134/recursively-rename-files-change-extension-in-linux
## https://stackoverflow.com/questions/6509650/extract-directory-from-path
## https://stackoverflow.com/questions/6121091/get-file-directory-path-from-file-path/6121114
## https://stackoverflow.com/questions/13210880/replace-one-substring-for-another-string-in-shell-script
## https://tldp.org/LDP/abs/html/string-manipulation.html
## https://stackoverflow.com/questions/16623835/remove-a-fixed-prefix-suffix-from-a-string-in-bash
## https://unix.stackexchange.com/questions/311758/remove-specific-word-in-variable
## https://unix.stackexchange.com/questions/56810/adding-text-to-filename-before-extension
## https://stackoverflow.com/questions/45799657/bash-adding-a-string-to-file-name
## https://linuxize.com/post/bash-functions/
## https://bash.cyberciti.biz/guide/Pass_arguments_into_a_function
## https://stackoverflow.com/questions/6212219/passing-parameters-to-a-bash-function
## https://linuxacademy.com/blog/linux/conditions-in-bash-scripting-if-statements/
################################
declare -a STRINGS_TO_REPLACE
STRINGS_TO_REPLACE=("tag1" "tag2")
STRING_TO_ADD_IF_NOT_PRESENT="tag3"
################################
rename_files_remove_old_tags_arguments() {
filepathnodot="${1#.}"
# echo "$filepathnodot"
justfilenamenopath="${1##*/}"
# echo "$justfilenamenopath"
justpathnofile=${1%/*}
# echo "$justpathnofile"
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$justfilenamenopath" == *"$current_string"* ]];
then
# echo "Will rename $justfilenamenopath"
test -e "$1" &&
newfilename=$(echo "$justfilenamenopath" | sed "s/$current_string//g")
mv -v "$1" "$justpathnofile/$newfilename"
break;
fi
done
}
rename_files_remove_old_tags_arguments
################################
rename_files_remove_old_tags() {
while IFS= read -r -d '' n; do
filepathnodot="${n#.}"
# echo "$filepathnodot"
justfilenamenopath="${n##*/}"
# echo "$justfilenamenopath"
justpathnofile=${n%/*}
# echo "$justpathnofile"
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$justfilenamenopath" == *"$current_string"* ]];
then
# echo "Will rename $justfilenamenopath"
test -e "$n" &&
newfilename=$(echo "$justfilenamenopath" | sed "s/$current_string//g")
mv -v "$n" "$justpathnofile/$newfilename"
break;
fi
done
done < <(find . \( -type f -name "[!.]*" \) -print0)
}
rename_files_remove_old_tags
################################
rename_folders_dirs_remove_old_tags() {
while IFS= read -r -d '' n; do
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$n" == *"$current_string"* ]];
then
# echo "Will rename $n"
test -e "$n" &&
newfilename=$(echo "$n" | sed "s/$current_string//g")
mv -v "$n" "$newfilename"
break;
fi
done
done < <(find . \( -type d -name "[!.]*" \) -print0)
}
rename_folders_dirs_remove_old_tags
################################
rename_files_add_new_tags() {
while IFS= read -r -d '' n; do
filepathnodot="${n#.}"
# echo "$filepathnodot"
justfilenamenopath="${n##*/}"
# echo "$justfilenamenopath"
justpathnofile=${n%/*}
# echo "$justpathnofile"
if [[ ! "$justfilenamenopath" == *"$STRING_TO_ADD_IF_NOT_PRESENT"* ]];
then
# echo "Will rename $justfilenamenopath"
test -e "$n" &&
newfilename="${justfilenamenopath%.*} $STRING_TO_ADD_IF_NOT_PRESENT.${justfilenamenopath##*.}"
mv -v "$n" "$justpathnofile/$newfilename"
fi
done < <(find . \( -type f -name "[!.]*" \) -print0)
}
rename_files_add_new_tags
################################
rename_folders_dirs_add_new_tags() {
while IFS= read -r -d '' n; do
filepathnodot="${n#.}"
# echo "$filepathnodot"
justpathnofile=${n%/*}
# echo "$justpathnofile"
if [[ ! "$n" == *"$STRING_TO_ADD_IF_NOT_PRESENT"* ]];
then
test -e "$n" &&
newfilename="$n $STRING_TO_ADD_IF_NOT_PRESENT"
mv -v "$n" "$newfilename"
fi
done < <(find . \( -type d -name "[!.]*" \) -print0)
}
rename_folders_dirs_add_new_tags
################################
################################
################################
rename_files_and_folders_dirs_1 () {
while IFS= read -r -d '' n; do
if [[ -f $n ]];
then
# echo "FILE <<< $n"
rename_files_remove_old_tags_arguments "$n"
# rename_files_add_new_tags "$n"
elif [[ -d "$n" ]];
then
echo "DIR >>> $n"
# rename_folders_dirs_remove_old_tags "$n"
# rename_folders_dirs_add_new_tags "$n"
fi
done < <(find . \( -name "[!.]*" \) -print0)
}
# rename_files_and_folders_dirs_1
################################
################################
################################
这是我迄今为止提出的一个可行的解决方案,需要优化和清理,它是基本的,我可以使用优化技巧:
#!/bin/bash
set -e
################################
## WORKING
##
## TODO: optimise
##
## WIP: [optimisation 1:]
## Instead of each function looping and going through all files and folders
## have one function to do the loop and pass the files and folders as arguments
## to all the other functions.
## Continue developing function `rename_files_and_folders_dirs_1`.
##
## [optimisation 2:]
## Expand features to take user input.
## For example call the script with flags/arguments/parameters/options
## which invoke different functions passing different arguments
## such as tags to remove or tags to add.
## Note: This could likely make [optimisation 1:] redundant, so need to choose
## witch path to follow.
##
## References
## https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
## https://stackoverflow.com/questions/15012631/rename-files-and-directories-recursively-under-ubuntu-bash
## https://superuser.com/questions/213134/recursively-rename-files-change-extension-in-linux
## https://stackoverflow.com/questions/6509650/extract-directory-from-path
## https://stackoverflow.com/questions/6121091/get-file-directory-path-from-file-path/6121114
## https://stackoverflow.com/questions/13210880/replace-one-substring-for-another-string-in-shell-script
## https://tldp.org/LDP/abs/html/string-manipulation.html
## https://stackoverflow.com/questions/16623835/remove-a-fixed-prefix-suffix-from-a-string-in-bash
## https://unix.stackexchange.com/questions/311758/remove-specific-word-in-variable
## https://unix.stackexchange.com/questions/56810/adding-text-to-filename-before-extension
## https://stackoverflow.com/questions/45799657/bash-adding-a-string-to-file-name
## https://linuxize.com/post/bash-functions/
## https://bash.cyberciti.biz/guide/Pass_arguments_into_a_function
## https://stackoverflow.com/questions/6212219/passing-parameters-to-a-bash-function
## https://linuxacademy.com/blog/linux/conditions-in-bash-scripting-if-statements/
################################
declare -a STRINGS_TO_REPLACE
STRINGS_TO_REPLACE=("tag1" "tag2")
STRING_TO_ADD_IF_NOT_PRESENT="tag3"
################################
rename_files_remove_old_tags_arguments() {
filepathnodot="${1#.}"
# echo "$filepathnodot"
justfilenamenopath="${1##*/}"
# echo "$justfilenamenopath"
justpathnofile=${1%/*}
# echo "$justpathnofile"
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$justfilenamenopath" == *"$current_string"* ]];
then
# echo "Will rename $justfilenamenopath"
test -e "$1" &&
newfilename=$(echo "$justfilenamenopath" | sed "s/$current_string//g")
mv -v "$1" "$justpathnofile/$newfilename"
break;
fi
done
}
rename_files_remove_old_tags_arguments
################################
rename_files_remove_old_tags() {
while IFS= read -r -d '' n; do
filepathnodot="${n#.}"
# echo "$filepathnodot"
justfilenamenopath="${n##*/}"
# echo "$justfilenamenopath"
justpathnofile=${n%/*}
# echo "$justpathnofile"
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$justfilenamenopath" == *"$current_string"* ]];
then
# echo "Will rename $justfilenamenopath"
test -e "$n" &&
newfilename=$(echo "$justfilenamenopath" | sed "s/$current_string//g")
mv -v "$n" "$justpathnofile/$newfilename"
break;
fi
done
done < <(find . \( -type f -name "[!.]*" \) -print0)
}
rename_files_remove_old_tags
################################
rename_folders_dirs_remove_old_tags() {
while IFS= read -r -d '' n; do
for current_string in "${STRINGS_TO_REPLACE[@]}" ;
do
if [[ "$n" == *"$current_string"* ]];
then
# echo "Will rename $n"
test -e "$n" &&
newfilename=$(echo "$n" | sed "s/$current_string//g")
mv -v "$n" "$newfilename"
break;
fi
done
done < <(find . \( -type d -name "[!.]*" \) -print0)
}
rename_folders_dirs_remove_old_tags
################################
rename_files_add_new_tags() {
while IFS= read -r -d '' n; do
filepathnodot="${n#.}"
# echo "$filepathnodot"
justfilenamenopath="${n##*/}"
# echo "$justfilenamenopath"
justpathnofile=${n%/*}
# echo "$justpathnofile"
if [[ ! "$justfilenamenopath" == *"$STRING_TO_ADD_IF_NOT_PRESENT"* ]];
then
# echo "Will rename $justfilenamenopath"
test -e "$n" &&
newfilename="${justfilenamenopath%.*} $STRING_TO_ADD_IF_NOT_PRESENT.${justfilenamenopath##*.}"
mv -v "$n" "$justpathnofile/$newfilename"
fi
done < <(find . \( -type f -name "[!.]*" \) -print0)
}
rename_files_add_new_tags
################################
rename_folders_dirs_add_new_tags() {
while IFS= read -r -d '' n; do
filepathnodot="${n#.}"
# echo "$filepathnodot"
justpathnofile=${n%/*}
# echo "$justpathnofile"
if [[ ! "$n" == *"$STRING_TO_ADD_IF_NOT_PRESENT"* ]];
then
test -e "$n" &&
newfilename="$n $STRING_TO_ADD_IF_NOT_PRESENT"
mv -v "$n" "$newfilename"
fi
done < <(find . \( -type d -name "[!.]*" \) -print0)
}
rename_folders_dirs_add_new_tags
################################
################################
################################
rename_files_and_folders_dirs_1 () {
while IFS= read -r -d '' n; do
if [[ -f $n ]];
then
# echo "FILE <<< $n"
rename_files_remove_old_tags_arguments "$n"
# rename_files_add_new_tags "$n"
elif [[ -d "$n" ]];
then
echo "DIR >>> $n"
# rename_folders_dirs_remove_old_tags "$n"
# rename_folders_dirs_add_new_tags "$n"
fi
done < <(find . \( -name "[!.]*" \) -print0)
}
# rename_files_and_folders_dirs_1
################################
################################
################################