我有一个巨大的文件档案,我在我的老式BBS上托管。[Mystic]软件不像带有长文件名或扩展字符的Linux那样宽容或有能力。
文件名的长度应少于 80 个字符。
文件名只能包含字符 A-Z 和 1-9。不"!@ # $ % ^ &" 等 - 也不是带有波浪号或插入符号的字母。
下面是一个集合目录的示例:
pi@bbs:/mnt/Beers4TB/opendirs/TDC19 $ ls -all
total 28
drwxrwxr-x 6 pi pi 4096 Sep 16 08:08 .
drwxrwxr-x 11 pi pi 4096 Oct 6 15:04 ..
drwxrwxr-x 2 pi pi 4096 Sep 13 20:13 ANSi
drwxrwxr-x 2 pi pi 4096 Oct 6 21:16 Drivers
drwxrwxr-x 10 pi pi 4096 Sep 16 08:12 Games
-rw-rw-r-- 1 pi pi 1056 Sep 13 20:12 INTRO.TXT
drwxrwxr-x 2 pi pi 4096 Sep 16 08:08 ListsNotes
在/subdirectory 中,它们可能会深入 2、3 或更多。
以下是某些文件当前命名的示例:
pi@bbs:/mnt/Beers4TB/opendirs/TDC19/Games/Applications $ ls M*
'Mean 18 - Golf Menu [SW] (1988)(Robert J. Butler) [Sports, Golf, Utility].zip'
'Mean 18 - M18 (1988)(Ken Hopkins) [Sports, Golf, Utility].zip'
'Metaltech- Battledrome Game Editor (1994)(Sierra On-Line, Inc.) [Utility].zip'
'Might and Magic III Character Editor (1991)(Blackbeard'''s Ghost) [Utility].zip'
'Might Magic 3 Character viewer-editor v1.1 (1991)(Mark Betz and Chris Lampton) [Editor].zip'
我做过一些有希望的事情......此 echo/sed 命令删除了一些高字符:
echo "Might and Magic III Character Editor (1991)(Blackbeard'''s Ghost) [Utility].zip" | sed -r -e 's/x27+//g' -e 's/[][")(]//g' -e 's/[ ]+//g'
(它重命名文件:) Might_and_Magic_III_Character_Editor_1991_Blackbeards_Ghost_Utility.zip
然后,我有一个命令可以重命名整个/subdirectory,但它不会删除任何字符:
for f in *.zip; do mv "${f}" "${f//[][")( ]/_}"; done
很好。。。但我必须摆脱高字符...而且,此方法有时会在文件名中添加多个空格 - 这增加了最大 80 个文件名限制 - 并且没有内置的 safegaurd......
我致力于添加对通过多个/subdirectory 的支持,但我知道我的语法仍然是错误的......但是,您可以看到我试图做什么:
P=$(pwd); for D in $(find . -maxdepth 1 -type d); do cd $D; for f in *.zip; do mv "${f}" "${f//[][")( ]/_}"; cd $P; done
所以,最后 - 我对任何 Linux 命令持开放态度,这些命令将: 删除所有不是 A-Z 或 1-9 的字符。 删除文件名中的任何多余空格。 确保文件名最多只有 80 个字符长,只需删除 .zip(或 .anything)扩展名之前的最后一位即可。 从主/目录开始,重命名主目录中每个/subdirectory 中的所有文件。
最后;我总是试图先把事情放在一起...我其次从同事那里得到帮助 - 我最后来到互联网......但我想了解如何自己编写这种确切的东西。如果您对在哪里学习有任何建议,那也会受到很好的欢迎。这次我试图正确发布这个问题,如果我没有正确制定每条规则,请原谅。
宝利42o . . . . ./s
命令tr -cd
删除所有不在给定列表中的字符。
for f in *.zip; do
mv "$f" "$(tr -cd 'A-Za-z0-9. n' <<< "$f")"
done
您可以使用sed
在相邻括号之间添加空格:
for f in *.zip; do
mv "$f" "$(sed 's/)(/ /g' <<< "$f" | tr -cd 'A-Za-z0-9. n'))"
done
您可以使用sed
合并多个空格。
for f in *.zip; do
mv "$f" "$(sed 's/)(/ /g' <<< "$f" | tr -cd 'A-Za-z0-9. n' | sed 's/ +/ /g'))"
done
我建议您对名称进行编码,但我没有适当的方法(足够的答案)来减少文件的长度以适应 80 个字符长(punycode 过程是完全可逆的,并将 ASCII 代码保留在其位置,为您提供可读的文件名,并且可以对其进行修改以考虑名称字符的字符大小写)
对于额外的长度编码,我会使用某种固定长度的哈希函数来避免名称冲突,但这个过程根本不是可逆的,您将丢失部分名称。 您需要考虑一下您的可能性,以便能够在这方面为您提供帮助。
编辑:将不需要的字符序列转换为一个下划线。
我假设当你写">文件名应该只有字符A-Z和1-9"时,你包括小写字母,加上下划线来替换任何不需要的字符序列。我还假设您不希望替换后在基本名称中使用前导或尾随下划线。
让我们首先编写一个小的bash脚本文件,它首先将zip文件的路径作为唯一的参数($1
),用dirname
和basename
分隔目录($d
)和文件($f
)部分,用tr
、sed
和cut
计算新的文件名,并重命名文件:
$ cat /mnt/Beers4TB/opendirs/TDC19/renamer.sh
#!/usr/bin/env bash
d="$(dirname "$1")"
f="$(basename -s .zip "$1" | tr -c a-zA-Z1-9 _ | sed 's/__*/_/g' |
cut -c 1-76 | sed 's/^_//;s/_$//')"
mv "$1" "$d/$f.zip"
接下来,让我们使脚本可执行(chmod
),并使用find
遍历层次结构并在每个找到的 zip 文件上调用脚本(首先备份您的文件,以防万一出现问题):
$ cd /mnt/Beers4TB/opendirs/TDC19
$ chmod +x renamer.sh
$ find . -type f -name '*.zip' -exec ./renamer.sh '{}' ;
(在exec
操作中,find
{}
被找到的文件路径替换)。
解释:
tr
用于用下划线 (_
) 替换所有不需要的字符。选项-c
采用指定字符集的补码:$ f='!!!Mean 18 - Golf Menu [SW] (' $ printf '%s' "$f" | tr -c a-zA-Z1-9 _ ___Mean_18___Golf_Menu__SW___
sed
用于仅用一个下划线(s/__*/_/g
)替换下划线序列,删除前导下划线(s/^_//
)和删除尾随下划线(s/_$//
):$ f="___Mean_18___Golf_Menu__SW___" $ printf '%s' "$f" | sed 's/__*/_/g' _Mean_18_Golf_Menu_SW_ $ f="_Mean_18_Golf_Menu_SW_" $ printf '%s' "$f" | sed 's/^_//;s/_$//' Mean_18_Golf_Menu_SW
cut
用于将修改后的基本名称裁剪为 80-4=76 个字符。恢复.zip
后缀后,它最多有 80 个字符。cut
的-c X-Y
选项选择字符编号X
Y
:$ f='abcdefghi' $ printf '%s' "$f" | cut -c 1-4 abcd
使用while
+read
循环、进程替换和find
加mv
来重命名文件。
脚本。
#!/usr/bin/env bash
shopt -s extglob nullglob
while IFS= read -rd '' directory; do
if [[ -e $directory && -x $directory ]] ; then
(
printf 'Entering directory %sn' "$directory"
cd "$directory" || exit
files=(*.zip)
(( ${#files[*]} )) || {
printf 'There are no files ending in *.zip here!, moving on...n'
continue
}
for file_name_with_extension in *.zip; do
extension=${file_name_with_extension##*.}
file_name_without_extension=${file_name_with_extension%."$extension"}
change_spaces_to_underscore="${file_name_without_extension//+([[:space:]])/_}"
remove_everything_that_is_not_alnum_and_under_score="${change_spaces_to_underscore//[![:alnum:]_]}"
change_every_underscore_with_a_single_under_score="${remove_everything_that_is_not_alnum_and_under_score//+(_)/_}"
new_file_name="$change_every_underscore_with_a_single_under_score.$extension"
mv -v "$file_name_with_extension" "${new_file_name::80}"
done
)
fi
done < <(find . ! -name . -type d -print0)
用于创建虚拟目录和文件的脚本。
#!/usr/bin/env bash
mkdir -p foo/bar/baz/more/qux/sux
cd foo/ && touch 'Mean 18 - Golf Menu [SW] (1988)(Robert J. Butler) [Sports, Golf, Utility].zip'
cd bar/ && touch 'Mean 18 - M18 (1988)(Ken Hopkins) [Sports, Golf, Utility].zip'
cd baz/ && touch 'Metaltech- Battledrome Game Editor (1994)(Sierra On-Line, Inc.) [Utility].mp4'
cd more/ && touch 'Might and Magic III Character Editor (1991)(Blackbeard'''s Ghost) [Utility].zip'
cd qux/ && touch 'Might Magic 3 Character viewer-editor v1.1 (1991)(Mark Betz and Chris Lampton) [Editor].zip'
cd sux/ && touch 'Might Magic 3 Character viewer-editor v1.1 (1991)(Mark Betz and Chris Lampton) [Editor].jpg'
使用tree
检查目录树
tree foo/
foo/
├── bar
│ ├── baz
│ │ ├── Metaltech- Battledrome Game Editor (1994)(Sierra On-Line, Inc.) [Utility].mp4
│ │ └── more
│ │ ├── Might and Magic III Character Editor (1991)(Blackbeard's Ghost) [Utility].zip
│ │ └── qux
│ │ ├── Might Magic 3 Character viewer-editor v1.1 (1991)(Mark Betz and Chris Lampton) [Editor].zip
│ │ └── sux
│ │ └── Might Magic 3 Character viewer-editor v1.1 (1991)(Mark Betz and Chris Lampton) [Editor].jpg
│ └── Mean 18 - M18 (1988)(Ken Hopkins) [Sports, Golf, Utility].zip
└── Mean 18 - Golf Menu [SW] (1988)(Robert J. Butler) [Sports, Golf, Utility].zip
5 directories, 6 files
使用find
打印文件。
find foo/ ! -name . -type f
<小时 />输出为
foo/Mean 18 - Golf Menu [SW] (1988)(Robert J. Butler) [Sports, Golf, Utility].zip
foo/bar/Mean 18 - M18 (1988)(Ken Hopkins) [Sports, Golf, Utility].zip
foo/bar/baz/more/Might and Magic III Character Editor (1991)(Blackbeard's Ghost) [Utility].zip
foo/bar/baz/more/qux/sux/Might Magic 3 Character viewer-editor v1.1 (1991)(Mark Betz and Chris Lampton) [Editor].jpg
foo/bar/baz/more/qux/Might Magic 3 Character viewer-editor v1.1 (1991)(Mark Betz and Chris Lampton) [Editor].zip
foo/bar/baz/Metaltech- Battledrome Game Editor (1994)(Sierra On-Line, Inc.) [Utility].mp4
在顶级目录中运行脚本,打印如下内容:
Entering directory ./foo
mv -v Mean 18 - Golf Menu [SW] (1988)(Robert J. Butler) [Sports, Golf, Utility].zip Mean_18_Golf_Menu_SW_1988Robert_J_Butler_Sports_Golf_Utility.zip
Entering directory ./foo/bar
mv -v Mean 18 - M18 (1988)(Ken Hopkins) [Sports, Golf, Utility].zip Mean_18_M18_1988Ken_Hopkins_Sports_Golf_Utility.zip
Entering directory ./foo/bar/baz
There are no files ending in *.zip here!, moving on...
Entering directory ./foo/bar/baz/more
mv -v Might and Magic III Character Editor (1991)(Blackbeard's Ghost) [Utility].zip Might_and_Magic_III_Character_Editor_1991Blackbeards_Ghost_Utility.zip
Entering directory ./foo/bar/baz/more/qux
mv -v Might Magic 3 Character viewer-editor v1.1 (1991)(Mark Betz and Chris Lampton) [Editor].zip Might_Magic_3_Character_viewereditor_v11_1991Mark_Betz_and_Chris_Lampton_Editor.
Entering directory ./foo/bar/baz/more/qux/sux
There are no files ending in *.zip here!, moving on...
如果您对
- 输出感到满意,请删除
echo
,以便mv
重命名文件。
如果没有echo
,输出如下所示:
Entering directory ./foo
renamed 'Mean 18 - Golf Menu [SW] (1988)(Robert J. Butler) [Sports, Golf, Utility].zip' -> 'Mean_18_Golf_Menu_SW_1988Robert_J_Butler_Sports_Golf_Utility.zip'
Entering directory ./foo/bar
renamed 'Mean 18 - M18 (1988)(Ken Hopkins) [Sports, Golf, Utility].zip' -> 'Mean_18_M18_1988Ken_Hopkins_Sports_Golf_Utility.zip'
Entering directory ./foo/bar/baz
There are no files ending in *.zip here!, moving on...
Entering directory ./foo/bar/baz/more
renamed 'Might and Magic III Character Editor (1991)(Blackbeard'''s Ghost) [Utility].zip' -> 'Might_and_Magic_III_Character_Editor_1991Blackbeards_Ghost_Utility.zip'
Entering directory ./foo/bar/baz/more/qux
renamed 'Might Magic 3 Character viewer-editor v1.1 (1991)(Mark Betz and Chris Lampton) [Editor].zip' -> 'Might_Magic_3_Character_viewereditor_v11_1991Mark_Betz_and_Chris_Lampton_Editor.'
Entering directory ./foo/bar/baz/more/qux/sux
There are no files ending in *.zip here!, moving on...
如果我们可以将不需要的字符序列转换为一个下划线,那就更好了。例如,而不是:XArchRogueTool(1984)(未知)[实用工具].zip输出可以是:
X_Arch_Rogue_Tool_(1984)_(Unknown)_[Utility].zip?
更改remove_everything_that_is_not_alnum_and_under_score
的值
从:
remove_everything_that_is_not_alnum_and_under_score="${change_spaces_to_underscore//[![:alnum:]_]}"
自
remove_everything_that_is_not_alnum_and_under_score="${change_spaces_to_underscore//[![:alnum:]_()[]]}"
排除括号( )
和括号[ ]
将代码添加到change_every_underscore_with_a_single_under_score
所在的行下方。
insert_underscore_in_between_parens="${change_every_underscore_with_a_single_under_score//')('/')_('}"
将new_file_name=
的值更改为"$insert_underscore_in_between_parens.$extension"
new_file_name="$insert_underscore_in_between_parens.$extension"
将目录指向脚本需要进行一些修改。
在shebang
后添加下面的代码
directory_to_process="$1"
if [[ ! -e "$directory_to_process" ]]; then
printf >&2 '%s no such file or directory!n' "$directory_to_process"
exit 1
elif [[ ! -d "$directory_to_process" ]]; then
printf >&2 '%s does not appear to be a directory!n' "$directory_to_process"
exit 1
fi
然后将.
从find
find "$directory_to_process" ! -name . -type d -print0
新脚本。
#!/usr/bin/env bash
directory_to_process="$1"
if [[ ! -e "$directory_to_process" ]]; then
printf >&2 '[%s] no such file or directory!n' "$directory_to_process"
exit 1
elif [[ ! -d "$directory_to_process" ]]; then
printf >&2 '[%s] does not appear to be a directory!n' "$directory_to_process"
exit 1
fi
shopt -s extglob nullglob
while IFS= read -rd '' directory; do
if [[ -e $directory && -x $directory ]] ; then
(
printf 'Entering directory %sn' "$directory"
cd "$directory" || exit
files=(*.zip)
(( ${#files[*]} )) || {
printf 'There are no files ending in *.zip here!, moving on...n'
continue
}
for file_name_with_extension in *.zip; do
extension=${file_name_with_extension##*.}
file_name_without_extension=${file_name_with_extension%."$extension"}
change_spaces_to_underscore="${file_name_without_extension//+([[:space:]])/_}"
remove_everything_that_is_not_alnum_and_under_score="${change_spaces_to_underscore//[![:alnum:]_()[]]}"
change_every_underscore_with_a_single_under_score="${remove_everything_that_is_not_alnum_and_under_score//+(_)/_}"
insert_underscore_in_between_parens="${change_every_underscore_with_a_single_under_score//')('/')_('}"
new_file_name="$insert_underscore_in_between_parens.$extension"
echo mv -v "$file_name_with_extension" "${new_file_name:0:80}"
done
)
fi
done < <(find "$directory_to_process" ! -name . -type d -print0)
现在你把目录作为脚本的参数。
./script.sh foo/
或绝对路径。
./script.sh /path/to/foo
如果将脚本添加到 PATH 并使其可执行,则可以。
script.sh /path/to/foo
假设您的脚本名称为script.sh
,并且要处理的目录名为foo
如果需要,将
80
的值更改为较低的值。查看
help continue
和help test
请参见参数扩展
来自
find
(1) 的 -print0 是 GNU 和 *BSD 的特性。请参阅如何逐行(和/或逐字段)读取文件(数据流、变量)?
请参阅如何检查目录是否为空?如何检查是否有任何 *.mpg 文件,或计算有多少个文件?
如果您的
mv
(1) 支持-n
标志,那就很好用了。