MrF*_*inn 4 command-line bash scripts files directory
我在一个目录中有数千个文件,我想在如下目录中整理这些文件:
由此:
??? Files
??? AAA.mkv
??? AAA.nfo
??? AAA-picture.jpg
??? BBB.mp4
??? BBB.srt
??? BBB-clip.mp4
??? CCC.avi
??? CCC.srt
??? CCC-clip.mov
??? CCC.nfo
Run Code Online (Sandbox Code Playgroud)
对此:
??? Files
??? AAA
? ??? AAA.mkv
? ??? AAA.nfo
? ??? AAA-picture.jpg
??? BBB
? ??? BBB.mp4
? ??? BBB.srt
? ??? BBB-clip.mp4
??? CCC
??? CCC.avi
??? CCC.srt
??? CCC-clip.mov
??? CCC.nfo
Run Code Online (Sandbox Code Playgroud)
文件名的长度和字数各不相同,有时用空格分隔,也可能有一些用连字符分隔(除了以“-short”结尾的那些。它们主要是具有各种格式/容器的视频文件:mov/mpg/ mkv/mp4/avi/ogg。有些是带字幕的。有些是带有相关元数据的文件(.nfo 或 -clip)
编辑:主要文件是视频(这是我想绘制目录名称的地方)。关联的文件代表元数据。仅通过扩展名命名的一些不同。基本文件名有六种其他变体,例如 -clip.mp4 -clip.mov 或 -picture.jpg 我想如果对这些少数人提出了一些建议,那么我可以(希望)找出其余部分。总之,AAA.mkv 移动到名为 AAA 的目录中。然后所有以 AAA 开头的元数据文件都加入它(即,在本例中:AAA-picture.jpg 和 AAA.nfo)。因此,在 AAA-picture.jpg 文件的情况下,基本名称实际上是一个子字符串。我会说简单地使用连字符作为分隔因子可能相对安全......尽管'-clip'或'-picture'整体会更安全。
我怎样才能在不患腕管综合症的情况下做到这一点?我看着这个,但它足够不同,我弱的脚本能力失败了。
谢谢你。
由于来自 OP、@dannysauer、@Arronical 和 @Scott 的评论,我制作了一个小的 bash 脚本来做到这一点,简化和改进
#!/bin/bash
for file in *
do mkdir -p "${file%%[.-]*}" 2>/dev/null
if [[ -d "${file%%[.-]*}" ]]; then
if [[ -f "$file" ]]; then
echo mv -v -- "$file" "${file%%[.-]*}"
fi
fi
done
Run Code Online (Sandbox Code Playgroud)
echo首先运行,然后删除echo以实际移动文件。必须从要移动文件的目录运行该脚本。如果你愿意,这里是一个单行命令:
for file in *; do mkdir -p "${file%%[.-]*}"; if [[ -d "${file%%[.-]*}" ]]; then if [[ -f "$file" ]]; then echo mv -v -- "$file" "${file%%[.-]*}"; fi ; fi ; done
Run Code Online (Sandbox Code Playgroud)
(再次,echo测试后删除)
for file in *; do mkdir -p "${file%%[.-]*}"使用每个文件名称的第一部分的名称创建一个目录(直到第一个连字符或点字符)-p标志在这里非常重要 - 没有它,脚本将只移动第一个匹配的文件(感谢 Arronical指出这-p将停止mkdir尝试创建现有目录并抱怨它)2>/dev/null 脚本抱怨它无法创建与自身同名的目录(但仍然有效),因此我们丢弃错误 - 作为单行运行时不需要这样做if [[ -d "${file%%[.-]*}" ]]; then如果有一个具有该名称的目录(如果mkdir成功)那么......if [[ -f "$file" ]] 如果我们正在处理一个文件(不是目录或其他东西),那么......mv -v -- "$file" "${file%%[.-]*}" 将其移动到匹配的目录中。在一个小的python脚本上:
#!/usr/bin/env python3
import shutil
import os
import sys
dr = sys.argv[1]
for f in os.listdir(dr):
split = f.rfind("."); short = f.find("-")
if split != -1:
extension = f[split:]
newname = f[:short] if short != -1 else f[:split]
target = os.path.join(dr, newname)
if not os.path.exists(target):
os.mkdir(target)
shutil.move(os.path.join(dr, f), os.path.join(target, f))
Run Code Online (Sandbox Code Playgroud)
要使用它:
将其复制到一个空文件中
将其另存为 move_into.py
以目录为参数运行它:
python3 /path/to/move_into.py /path/to/directory
Run Code Online (Sandbox Code Playgroud)
该脚本假定所有(相关)文件都有扩展名。如果文件没有扩展名,则不会发生任何事情。如果这是一个问题,请提及,可以轻松更改。
随后,文件被移动到相应的文件夹中。
虽然您的问题被标记为bash,但将其bash用于此类任务会有些麻烦(以我的拙见)。我建议使用 python,因为它为复杂任务提供了很多很好的功能,这个答案提供了使用该语言的解决方案。
基本上这里发生的是我们使用正则表达式在多个分隔符处拆分文件名,仅获取第一部分并使用这些第一部分的唯一集合作为新目录的基本名称。
然后我们再次遍历顶级目录,并在适当的位置对文件进行排序。
该脚本没有做任何引人注目的事情,实际上在算法分析中这不会做得太好,因为嵌套的 for 循环,但对于“快速而肮脏但可行”的解决方案,它没关系。如果您对每一行的作用感兴趣,可以添加大量注释来解释功能
请注意,该演示仅显示打印新文件名仅用于测试目的。取消注释该os.rename()部分以实际移动文件。
bash-4.3$ # Same directory structure as in OP example
bash-4.3$ ls TESTDIR
bash-4.3$ # now run script
AAA AAA.mkv AAA.nfo AAA-picture.jpg BBB BBB-clip.mp4 BBB.mp4 BBB.srt
bash-4.3$ ./collate_files.py ./TESTDIR
/home/xieerqi/TESTDIR/AAA/AAA-picture.jpg
/home/xieerqi/TESTDIR/AAA/AAA.mkv
/home/xieerqi/TESTDIR/AAA/AAA.nfo
/home/xieerqi/TESTDIR/BBB/BBB.srt
/home/xieerqi/TESTDIR/BBB/BBB.mp4
/home/xieerqi/TESTDIR/BBB/BBB-clip.mp4
Run Code Online (Sandbox Code Playgroud)
bash-4.3$ # Same directory structure as in OP example
bash-4.3$ ls TESTDIR
bash-4.3$ # now run script
AAA AAA.mkv AAA.nfo AAA-picture.jpg BBB BBB-clip.mp4 BBB.mp4 BBB.srt
bash-4.3$ ./collate_files.py ./TESTDIR
/home/xieerqi/TESTDIR/AAA/AAA-picture.jpg
/home/xieerqi/TESTDIR/AAA/AAA.mkv
/home/xieerqi/TESTDIR/AAA/AAA.nfo
/home/xieerqi/TESTDIR/BBB/BBB.srt
/home/xieerqi/TESTDIR/BBB/BBB.mp4
/home/xieerqi/TESTDIR/BBB/BBB-clip.mp4
Run Code Online (Sandbox Code Playgroud)
re.split()函数中)分割文件:在方括号内"[.-]"添加(意思是)添加您想要的任何字符。os.rename()功能。或者,您可以import shutil使用shutil.move()函数。见/sf/answers/620061851/