如何防止`mv`将文件集合移动到单个常规文件中?


17

由于我犯了一个愚蠢的错误,我失去了一部分音频收藏。:-(很
高兴我有一个最近的备份,但是它仍然很烦人。除了您的确如此,另一个作弊的罪魁祸首是mv,它将显示如下:

音频文件有一个特定的方案:

ARTIST - Some Title YY.mp3

YY2位数字的年份规范在哪里。

mkdir 90<invisible control character>

(到现在为止,我还不知道我实际上键入了三分之一的多余字符,这是不可见的……!)
我不想将所有1990年代的音乐都放在一个目录中,而不是全部放在一个目录中。所以我输入:

find . -name '* 9?.mp3' -exec mv {} 90 \;

不难理解发生了什么事吗?:->
(灾难性的)结果是一个名为'90 something ' 的原始目录(其中某些东西是“不可见的”控制字符)和一个名为'90'的单个文件,被覆盖了n次。

所有文件都消失了。:-(((显然)

希望mv能够及时检查目标“文件”的签名(请记住* NIX:Everything Is A File是否d------(例如drwxr-xr-x)开头。当然,目的地是否存在。当您只是简单地忘记首先mkdir进入目录时,上述情形就有一个变体。(但是,当然,您认为它在那里...)

甚至从首字母W开始的我们讨厌的OS都这样做。如果需要,甚至会提示您指定目标的类型(文件?目录?)。

因此,我想知道我们* NIXers是否仍必须为自己编写“ mv脚本”,以避免这些最不希望的意外。


2
并非所有文件都消失了。至少应有一个.mp3名称为的目录90,它可能是您没有备份的目录。
Anthon 2014年

2
嘿,你有一种愤世嫉俗的幽默感,你疯了!:-P嗯,这就是我的OP中以粗体显示的称为“单个文件”的文件。:)
语法错误

2
mv从技术上讲,这不是问题所在,它不知道您要移动一系列文件。mv每个文件运行一次。就是这样find -exec ;。如果您曾经使用过find -exec +(如某些评论中所述),则只要有一个以上的论点mv 便会大喊。
Etan Reisner,2014年

尽管一开始mv为每个文件运行似乎没有多考虑,但是(如我之前所说)它将是唯一的明智的解决方案,只要源文件分散在各个子目录中即可。在我的测试用例中,源文件全部放在一个目录中并不意味着这是我的实际测试用例。实际上,这只是一个简化,因为稍后我可能会很容易地对其进行详细说明。另外,由于其长度减少,因此阅读问题的时间更少。:)
语法错误

您为什么期望mv要求目的地存在?mv oldfile newfile是重命名文件的方法,期望newfile已经存在并成为目录是很愚蠢的。
Barmar 2014年

Answers:


37

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

mv somefile somedir/
mv: cannot move ‘somefile’ to ‘somedir/’: Not a directory

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


4
非常感谢!那应该是我未来的救星。我欠你一个人情。(仅作说明,我的一个朋友几年前犯了同样的错误,所以我并不孤单。)
语法错误

1
此外,在使用某些Shell时,您可以使用Tab选项为您自动完成文件名。如果我不太记得目录名,并且不想像您最近那样混乱,只需输入目录名和HIT TAB中的一两个字符即可。然后您可以确定它是否存在,因为自动完成功能已将其放置在那里
。...– Andyz Smith

@AndyzSmith好吧,就是这样。您可能会说这是我的习惯,只将TAB用于复杂的目录或路径,而不用于2字母类型的目录。:)但是,请考虑一下...也许我也应该考虑后一种情况从现在开始。
语法错误

20

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

我会这样写你的动子:

find . -name '* 9?.mp3' -exec mv -t 90 {} +

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


谢谢。(但是希望它不再是那些GNU主义之一。)
语法错误

2
@syntaxerror。这是一个GNUism。POSIXly:find . -name '* 9?.mp3' -exec sh -c 'exec mv "$@" 90/' sh {} +
斯特凡Chazelas

非常感谢您这个最偷偷摸摸的单线!只是认为这是我给您的+5。:)无需进行大量的反复试验。---而且您也很清楚我为什么这么说。只需要直接在POSIX上运行机器(就不会发生太少),而我将在那里遇到下一个问题。:)
语法错误

1
@syntaxerror如果此答案解决了您的问题,请花一点时间,然后单击左侧投票计数下的复选标记,这将向所有人表示您的问题已解决,这是在网站上表达感谢的方式。我看到您的其他回答问题中只有少数接受了答案,您可能也想收回那些问题。
Anthon 2014年

不,这只是目的。我经常要等待几周甚至两个月,直到我接受答案。原因是一些知识渊博的人安排的时间表非常多,可能只有两周后才有时间给出他们的答案(通常是最好的)。因此,我总是很恭敬地等待他们的到来。好吧,如果他们确实不这样做,我肯定会毫不犹豫地打勾号。此外,我不明白为什么有些人总是如此急于SE及其口味。伙计们,做起来容易。不要跳枪。:)这不是你的老板逼你。
语法错误

10

另外,如果您总体上打算避免以后再发生意外覆盖,则可以使用的-i选项mv。我个人认为如果您有任何弊端,

alias mv='mv -i'

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

仅当您直接在交互式shell中键入命令时,别名才会生效,而对于诸如由调用的情况则无效find。你本来可以跑

find . -name '* 9?.mp3' -exec mv -i {} 90 \;

如果mv试图覆盖现有文件,则会提示您。


4
或者-您可能不知道-输入´\ mv`而不是mv。这种鲜为人知的“技巧”将导致在命令前加上反斜杠,从而忽略任何别名定义。
语法错误

当然,如果您真正在谈论find ... -exec mv ...,那么您需要创建~/bin/mv(或其他适当的目录)并进行设置/bin/mv -i "$@"-因为find ... -exec它不会查看别名。
G-Man说'Resstate Monica''Nov

在这种情况下,我更喜欢env mv。打字少。:)因为我的本地键盘布局要求按下SHIFT键才能使用正斜杠,所以我将始终倾向于“无斜杠”版本(如果适用)。
语法错误

1
@syntaxerror:顺便说一句,当您回复评论(在新评论中)时,通常会提及作者的名字,并以“ @”开头,例如“ @ G-Man”。这样我会收到通知。(由于收到Jenny D的评论,我能够及时回复您的最新评论。)您可以缩写或使用全名(不带空格),例如“ @StéphaneChazelas”。帖子的作者会自动收到对该帖子的评论的通知。请参阅此帮助页面的“ 在评论中回复”段落。
G-Man说'Resstate Monica'2014/

1
对不起,我一天没有上网。@ G-Man我同意mv在开始的某个位置使用的个人版本$PATH将是一种更清洁的解决方案。另一方面,我主要是偶然地mv在交互式shell中发生(因为它发生得很快)。那一刻我撰写更复杂的东西,像findfor循环,甚至一个shell脚本,我倾向于执行(使用一些干运行echo),以确保我不会碰坏。在那些情况下,我不需要手动操作mv,因为我已经在考虑其中了。
ayekat 2014年

7

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

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

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

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

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


相反,如果您已完成mv *90.mp3 90,则mv将会失败,并显示错误消息“目标文件不是目录”。嗯,为什么如此复杂?我用你的电话,我会很高兴。不过,仅在这种琐碎的情况下。:)因为这是我问问题的正常方式:为了简单起见,请缩小范围。find到目前为止,还没有人反对将90年代的文件散布在我也想“捕获”的各个子目录中。当且当它们始终位于一个源目录中时,您的mv行才适用。
语法错误

@syntaxerror非常正确-我的意思是作为mv举止的一个示例,而不是批评您选择的工具。
珍妮·D

1
您可能可以构建一些结合find和的东西mv,例如find /music -type d -exec mv {}/*90.mp3 targetdir\;-但现在我觉得我有点过于复杂了,只是使用-i-t更有效
Jenny D

1
@Jenny:如果您的find . -type d -exec mv {}/*9?.mp3 target \;示例可行,则对于仅包含一个文件的每个目录,该mv命令仍将具有以下风险:因此所有这些文件(最后一个文件除外)都将丢失。mv file target*9?.mp3
G-Man说'恢复莫妮卡'

4
@syntaxerror:我赞扬您为解决问题的关键部分所做的努力,而不是暴露出现问题的整个42,000行脚本。但是,即使您确实想对*.mp3目录树中的所有文件做某事,也可以shopt -s globstar然后在其上运行命令**/*.mp3- **就像一样find
G-Man说'Resstate Monica'2014/

1

作为另一种通用策略,我建议将这种操作转换为临时脚本。我更喜欢查看结果find并将它们mv手动转换为命令,以确保在执行之前了解自己在做什么。例如。

find . -name '* 9?.mp3' > tmp
vim tmp

现在,我可以浏览文件名列表,并将其内容重写为Shell命令。

  • 将文件内容放在一行上: ggVGJ
  • 前置: Imv [esc]
  • 附加: Asomedir/ [esc]
  • 保存文件。再次阅读。喘口气。
  • source tmp在命令行上执行。

这是一种保守的策略,但是我经常被打错字眼-execsed键入命令,或者误解了Shell扩展而被咬伤,我更喜欢采用缓慢一致的方法。

换句话说:我太胆怯了,无法使用-exec


1
您省去了:%s/.*/"&"/步骤-因为在这种情况下,您知道每个文件名至少包含一个空格。
G-Man说'Resstate Monica''Nov

1
如果文件名包含空格或其他特殊字符,这会给您带来麻烦。您需要正确引用它们。查看命令列表不太可能会捕获错误。在运行命令之前,有更好的方法来查看命令,例如运行echo mv而不是mv,然后echo在满意时将其删除。
吉尔斯(Gilles)“所以,别再邪恶了”

准确地发现诸如文件名和空格之类的错误是这项技术可以帮助的:-)
Tom Rees 2014年

0

另外的选择:

-n,-no-clobber不会覆盖现有文件

它与-i相同,但不会询问,它将失败。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.