I'm trying to move a bunch of files from a directory to my phone in a specific order so it wouldn't mess up the order in my phone.
The furthest I've gotten is by using this command:
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'
or this one
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'
The problem is that once the filename includes an apostrophe, I get the following error:
xargs: unmatched single quote; by default quotes are special to xargs unless you use the -0 option
I understand that when I'm using "find" command I need to add a -print0, but doing this
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'
Gives me an error: find: paths must precede expression: `%T@ %p\n'
Turning the command into this works but messes up the order
find * -print0 -printf "%T@ %p\n" | sort -n | sed 's@^.*/@@'
Any help?
Best Answer
In this answer I'm considering the following command of yours as our starting point:
Then you write:
and then you try:
or:
I think the main misunderstanding is in "I need to add a
-print0
". No, you don't add-print0
. In cases (examples) where-print0
appears and seems to be added, it really replaces an implicit-print
. The point is-print
is the default action and in many cases you don't have to type it. Then, if you want to replace it with-print0
, you append-print0
. The existence of-print0
suppresses the default-print
. It looks as if-print0
was added, but logically it replaced-print
.In your case the
-printf …
already suppresses the default-print
. This means there is no-print
in your command. If you add-print0
(like you did), you will have-printf …
and-print0
and each one will print something.You don't want to simply replace your
-printf …
with-print0
, because for you the point of having-printf …
instead of (explicit or implicit)-print
is this%T
you used. You want to modify your-printf …
, so the new-printf …
with respect to the old-printf …
is exactly like-print0
with respect to-print
.What is the difference between
-print0
and-print
? The former uses one trailing null byte as the terminator, where the latter uses one trailing newline character.-print0
is equivalent to-printf '%p\0'
and-print
is equivalent to-printf '%p\n'
Your
-printf "%T@ %p\n"
exactly uses one trailing newline character, it's-print
-like. To make it-print0
-like and retain the%T
, you just need to change\n
into\0
:Then you need to make your
sort
,sed
andxargs
work with null-terminated lines. Hopefully if yourfind
is rich enough to support-print0
and-printf
then your other tools are also capable of handling null-terminated data. And I assume yourcp
supports-t
. Effectively I assume GNU tools.This is the command:
Note I changed the
sed
code. Your code removes everything up to the last/
(.*
is greedy!), so only the last component survives, the filename (except for.
where there is no/
, so everything survives!). If there are files matching-iname '*.mp3' -type f
in subdirectories then passing only the last component will makecp
fail. My code removes everything up to and including the first space, i.e. the fragment resulting from%T@
. Everything resulting from%p
survives. This means that:cp
overwrite, so eventually some files may be missing in the target location;-
are safe (i.e. we don't need double dash) because all pathnames start with.
anyway.I used
-iname '*.mp3'
because elsewhere you used*.mp3
. I used-type f
because its a sane thing to do (you certainly don't want to process paths to directories and pass them tocp
).I believe if there is a pathname that forms an invalid text in your locale then you need
LC_ALL=C
, for-iname '*.mp3'
to work as expected. I'm not sure aboutsed
. If you want to work with such pathnames, considerexport LC_ALL=C
beforehand.