我正尝试按照特定顺序将一堆文件从目录移动到我的手机中,这样就不会弄乱手机中的顺序。
我所能做的最好的就是使用这个命令:
find . -printf "%T@ %p\n" | sort -n | sed 's@^.*/@@' | xargs -I{} cp "{}" '/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RF8NA3063PJ/Internal storage/Music/music'
或者这个
stat -c "%Y/%n" *.mp3 | sort -t/ -k1,1n |sed 's@^.*/@@' | xargs -I{} cp "{}" '/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RF8NA3063PJ/Internal storage/Music/music'
问题是,一旦文件名包含撇号,我就会收到以下错误:xargs:不匹配的单引号;默认情况下,引号对 xargs 来说是特殊的,除非您使用 -0 选项
我知道当我使用“find”命令时,我需要添加 -print0,但是这样做
find . -print0 "%T@ %p\n" | sort -n | sed 's@^.*/@@' | xargs -I{} cp "{}" '/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RF8NA3063PJ/Internal storage/Music/music'
给我一个错误:查找:路径必须在表达式之前:'%T @%p \ n'
将命令转换为这样可以工作,但会打乱顺序
find * -print0 -printf "%T@ %p\n" | sort -n | sed 's@^.*/@@'
有什么帮助吗?
答案1
在这个答案中,我将以下命令视为我们的起点:
find . -printf "%T@ %p\n" | sort -n | sed 's@^.*/@@' | xargs -I{} cp …
然后你写:
我知道当我使用“查找”命令时,我需要添加一个
-print0
然后你尝试:
find . -print0 "%T@ %p\n" | …
或者:
find * -print0 -printf "%T@ %p\n" | …
我认为主要的误解在于“我需要添加不-print0
,你不需要添加-print0
。在-print0
出现和似乎被添加的情况下(例子),它真的取代隐含的-print
。重点是-print
是默认操作,在许多情况下您不必键入它。然后,如果您想用 替换它-print0
,您可以附加-print0
。 的存在-print0
会抑制默认的-print
。看起来好像-print0
是添加了,但从逻辑上讲它替换了-print
。
在您的情况下,-printf …
已经抑制了默认的。这意味着您的命令中-print
没有。如果您添加(就像您所做的那样),您将拥有和 ,并且每个都会打印一些内容。-print
-print0
-printf …
-print0
您不想简单地用 替换您的-printf …
,因为对您来说,使用代替(显式或隐式)-print0
的意义在于您使用的这个。您想-printf …
-print
%T
调整你的-printf …
,因此新的相-printf …
对于旧的就像是相对于的-printf …
完全一样。-print0
-print
-print0
和有什么区别-print
?前者使用一个尾随空字节作为终止符,而后者使用一个尾随换行符。-print0
相当于-printf '%p\0'
和-print
相当于-printf '%p\n'
您的 -printf "%T@ %p\n"
代码确实使用了一个尾随换行符,它是-print
-like 。要使其-print0
类似于 并保留%T
,您只需将其更改\n
为\0
:
find . -printf '%T@ %p\0'
然后你需要制作你的sort
,sed
并xargs
处理以空字符结尾的行。希望如果你的find
足够丰富以支持-print0
,-printf
那么你的其他工具也能够处理以空字符结尾的数据。我假设你cp
支持-t
。实际上我假设 GNU 工具。
这是命令:
find . -iname '*.mp3' -type f -printf '%T@ %p\0' \
| sort -zn | sed -z 's@^[^ ]* @@' \
| xargs -r0 cp -t '/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RF8NA3063PJ/Internal storage/Music/music'
注意我更改了sed
代码。您的代码删除了直到最后一个/
(.*
是贪婪的!) 的所有内容,因此只有最后一个组件保留下来,即文件名(除了.
没有 的地方/
,所以所有内容都保留下来!)。如果子目录中有匹配的文件,-iname '*.mp3' -type f
则仅传递最后一个组件将cp
失败。我的代码删除了直到第一个空格的所有内容,即 产生的片段%T@
。 产生的所有内容都%p
保留下来。这意味着:
- 子目录中的文件可以被复制,
- 但是名称冲突(如果有的话)会导致稍后的
cp
覆盖,因此最终目标位置可能会丢失某些文件; - 以 开头的路径名组件
-
是安全的(即我们不需要双划线) 因为所有路径名都以 开头.
。
我使用是-iname '*.mp3'
因为您在其他地方使用了*.mp3
。我使用是-type f
因为这样做是明智的(您当然不想处理目录路径并将它们传递给cp
)。
我相信如果在您的语言环境中有一个路径名构成无效文本,那么您需要LC_ALL=C
,才能-iname '*.mp3'
按预期工作。我不确定sed
。如果您想使用此类路径名,请export LC_ALL=C
事先考虑。
答案2
抱歉,我没有足够的声誉来发表评论,所以这里是我的评论:我在一个小的目录中测试了它,我看到find .
它返回了˙。˙,但其中没有/
,所以我认为你的 sed 搞砸了。这是我的测试:
find . -printf "%T@ %p\n" |sort -n
1650024944.2971079410 ./.nvmrc
1650108929.2884298980 ./package.json
1650108929.2884298980 ./package-lock.json
1657746711.8129748630 .
find . -printf "%T@ %p\n" |sort -n |sed 's@^.*/@@'
.nvmrc
package.json
package-lock.json
1657746711.8129748630 .
可能不是最有效的解决方案:
find . -printf "%T@ %p\n" |sort -n |grep -v ' \.$' |sed 's@^.*/@@'
.nvmrc
package.json
package-lock.json
更新:我认为这会起作用。你非常接近了,只是错过了-printf
你使用的位置-print0
。而且grep -v
解决方案不适用于-print0
,所以我添加了! -path .
find 以排除.
find . ! -path . -print0 -printf "%T@ %p\n" | sort -n | sed 's@^.*/@@' | xargs -I{} cp "{}" '/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RF8NA3063PJ/Internal storage/Music/music'
答案3
find . -type f -printf "%T@ %p\n" \
| sort -n \
| sed "s:^[^ ]* ::;s:\":\\\\\":g;s:^:cp -t target-directory \":;s:$:\":" \
| bash
解释:
- 寻找命令:
- -类型 f- 仅搜索文件,不搜索目录
- sed命令:
- s:^[^ ]* ::- 删除主要内容
- s:":\\\\\":g- 将双引号改为 \"
- s:^:cp -t 目标目录 \":-- 添加前缀
- s:$:\":- 并添加后缀
我必须使用管道bash
而不是使用,xargs
因为我无法处理 中的引号/双引号xargs
。很抱歉。请阅读此内容:https://unix.stackexchange.com/questions/385466/properly-escaping-output-from-pipe-in-xargs
(抱歉我的英语不好)