如何防止`mv`将一组文件移动到一个常规文件中?

syn*_*ror 17 mv

由于我犯的一个愚蠢的错误,我刚刚丢失了我的音频收藏的一小部分。:-( 很
高兴我有一个相当新的备份,但它仍然很烦人。除了你的真实之外,另一个做恶作剧的罪魁祸首是mv,它将显示如下:

音频文件有一定的方案:

ARTIST - Some Title YY.mp3
Run Code Online (Sandbox Code Playgroud)

其中YY是 2 位数年份规范。

mkdir 90<invisible control character>
Run Code Online (Sandbox Code Playgroud)

(直到此刻,我都不知道我实际上输入了三分之一看不见的多余字符......!)
我希望将所有 1990 年代的音乐都放在一个目录中,而不是将所有内容都放在一个目录中。所以我输入:

find . -name '* 9?.mp3' -exec mv {} 90 \;
Run Code Online (Sandbox Code Playgroud)

不难明白发生了什么,嗯?:->
(灾难性的)结果是一个名为“90 something ”的原始目录(某些东西是“隐形”控制字符)和一个名为“90”​​的单个文件,被覆盖了n次。

所有文件都不见了。:-(( (明显地)

Wishmv会及时检查目标“文件”的签名(请记住 *NIX: Everything Is A File是否d------(例如drwxr-xr-x)开头。而且,当然,目的地是否存在。有上述情况下,当你只是一个变种mkdir的目录第一。(当然,你假设它在那里......)

甚至我们以大写字母W开头的讨厌的操作系统也会这样做。如果您要求,甚至会提示您指定目标类型(文件?目录?)。

因此,我想知道我们 *NIXers 是否仍然必须为自己编写一个“mv脚本”来避免这些最不想要的惊喜。

Mar*_*rco 37

/如果要将文件移动到目录,可以将 a 附加到目标。如果目录不存在,您将收到错误消息:

mv somefile somedir/
mv: cannot move ‘somefile’ to ‘somedir/’: Not a directory
Run Code Online (Sandbox Code Playgroud)

如果目录存在,它会将文件移动到该目录中。

  • 非常感谢!那应该是我未来的救命稻草。我欠你一个人情。(顺便提一下,我的一个朋友几年前也犯过同样的错误,所以我觉得我并不孤单。) (4认同)

Ant*_*hon 20

GNU coreutilsmv已经有一个选项指定您要移动到目录:-t/ --target-directory。如果该选项的参数不存在,mv将抱怨而不是将所有文件移动到相同的文件名。

我会写你的推动者如下:

find . -name '* 9?.mp3' -exec mv -t 90 {} +
Run Code Online (Sandbox Code Playgroud)

请注意使用+代替\;,将尽可能多的文件名连接在一起,从而加快执行速度。

  • @语法错误。这是一种GNU主义。POSIXly:`找到。-name '* 9?.mp3' -exec sh -c 'exec mv "$@" 90/' sh {} +` (2认同)

aye*_*kat 10

此外,如果您通常打算避免将来发生意外覆盖,则可以-i选择mv. 我个人想不出任何缺点,如果你

alias mv='mv -i'
Run Code Online (Sandbox Code Playgroud)

如果您随后需要覆盖某些内容,只需传递该-f选项即可。

别名仅在您直接在交互式 shell 中键入命令时才生效,而不适用于通过find. 你可以跑

find . -name '* 9?.mp3' -exec mv -i {} 90 \;
Run Code Online (Sandbox Code Playgroud)

然后,如果您mv试图覆盖现有文件,系统会提示您。

  • 或者 - 你可能不知道 - 输入 ´ \mv ` 而不是 `mv`。这个鲜为人知的“技巧”将导致带有反斜杠的命令忽略*任何*别名定义。 (4认同)

Jen*_*y D 7

除了上述出色的答案之外,我想澄清一下为什么您没有收到有关是否移动文件的问题。

如果您将一个文件移动到一个新名称,并且该名称不是目录,mv则会将您的文件重命名为新名称。

这里的问题是您曾经对每个文件find执行mv一次,而不是对所有文件执行一次。

相反,如果您已完成mv *90.mp3 90,则将 mv失败并显示“目标文件不是目录”的错误消息。

另一条建议是在键入目标路径时使用制表符完成。它将通过添加/到目标名称来显示目标是否是目录。您还可以使用mv -i询问是否要覆盖现有文件。

  • @syntaxerror:我赞赏你努力揭露问题的关键部分,而不是出现问题的整个 42,000 行脚本。但是,即使你确实想对目录树中的所有 `*.mp3` 文件做一些事情,你也可以 `shopt -s globstar` 然后在 `**/*.mp3` 上运行你的命令——`* *` 会像一个 `find`。 (4认同)