18 votes

mv src/ vs mv src: merging subfolders with mv, cp, rsync, and rclone

Tags: linux

15 comments

  1. [4]
    xk3
    Link
    I spent an hour or so figuring out how different programs merge folders/subfolders with the same name. I did this by resetting the folder before running each command. Here are the results: Nested...

    I spent an hour or so figuring out how different programs merge folders/subfolders with the same name. I did this by resetting the folder before running each command.

    Here are the results:

    Nested subfolder (actual destination three/one/one):

    Full Command Source Parameter Destination Parameter
    mv one three/one one three/one
    cp -r one three/one && rm -rf one one three/one
    rsync -auh --remove-source-files one three/one one three/one
    rclone move -q --no-traverse one three/one/one one three/one/one

    Merged subfolder (actual destination three/one):

    Full Command Source Parameter Destination Parameter
    mv one/* three/one one/* three/one
    cp -r one three && rm -rf one one three
    rsync -auh --remove-source-files one three one three
    rclone move -q --no-traverse one three/one one three/one

    Merged destination (actual destination three):

    Full Command Source Parameter Destination Parameter
    mv one/* three one/* three
    cp -r one/* three && rm -rf one one/* three
    rsync -auh --remove-source-files one/ three one/ three
    rclone move -q --no-traverse one three one three

    I thought trailing slash mattered more, but it actually only matters in the "merged destination" instance.

    Out of all of these, I think rclone provides the least surprising result (the actual destination is the specified destination)--but rclone is a lot slower than mv in many scenarios and it should be noted that you can't rename files with rclone like you can with mv.

    10 votes
    1. [3]
      vord
      (edited )
      Link Parent
      I don't really find any of these cases confusing, or even terribly inconsistent. The way Docker and some other tools handle it is far worse. Having /* is a glom copying the contents of the...

      I don't really find any of these cases confusing, or even terribly inconsistent. The way Docker and some other tools handle it is far worse.

      Having /* is a glom copying the contents of the directory that gets silently expanded behind the scenes, not the directory itself. The trailing slash is otherwise always ignored. That does seem to be the source of much of the author's confusion.

      Perhaps some confusion derives from that rclone and rsync are intended to work primarily with directories while mv and cp are just as likely to be for files themselves, especially cp. There's a reason you need an extra flag to copy a directory with cp.

      6 votes
      1. [2]
        kovboydan
        Link Parent
        These feel intuitive to me now too, but I’m sure there was a time when they didn’t. The trailing slash is ignored by GNU cp but not by BSD cp, and rsync behaves like the BSD version. As far as I...

        These feel intuitive to me now too, but I’m sure there was a time when they didn’t.

        The trailing slash is ignored by GNU cp but not by BSD cp, and rsync behaves like the BSD version.

        As far as I know, best practice for interoperability between flavors of cp is to append a /. if your goal is to have the contents of sourceDir copied into destinationDir because the behavior of the BSD and GNU version of cp are the same for:

        cp -r sourceDir/. destinationDir

        Using dir/. instead of dir/* copies all files in a directory including hidden files - which I think we’re calling dot files these days - as well.

        Used thusly, cp and rsync have the same source and destination parameters in all three tables.

        6 votes
        1. vord
          Link Parent
          I guess this is true. I've mostly used GNU tooling outside a very brief stint with FreeBSD. Mac users would probably see mostly the reverse.

          I guess this is true. I've mostly used GNU tooling outside a very brief stint with FreeBSD. Mac users would probably see mostly the reverse.

          3 votes
  2. [3]
    kovboydan
    Link
    If anyone is curious, Section 2.1 of the rsync arch wiki covers the trailing slash behavior of rsync and its origin (BSD cp vs GNU cp).

    If anyone is curious, Section 2.1 of the rsync arch wiki covers the trailing slash behavior of rsync and its origin (BSD cp vs GNU cp).

    4 votes
    1. [2]
      xk3
      Link Parent
      Thanks for this comment! I wasn't aware that rsync will always create a subfolder if there is no trailing slash. That helps me understand it a bit more. You inspired me to do some more experiments...

      Thanks for this comment!

      I wasn't aware that rsync will always create a subfolder if there is no trailing slash. That helps me understand it a bit more.

      You inspired me to do some more experiments and add this to the document:

      When the destination doesn't exist:

      Full Command Source Parameter Actual Destination Result
      mv one two one two folder rename, same inode
      mv one/* two one/* x Error: target 'two': No such file or directory
      rclone move -q --no-traverse one two one two folder rename, same inode
      cp -r one two && rm -rf one one two new folder
      cp -r one/. two && rm -rf one one/. two new folder
      cp -r one/* two && rm -rf one one/* x Error: target 'two': No such file or directory
      rsync -auh --remove-source-files one/ two one/ two new folder
      rsync -auh --remove-source-files one two one two/one new folder, subfolder with new inode

      When the destination is an empty folder:

      Full Command Source Parameter Actual Destination Result
      mv one two one two/one folder rename, subfolder with same inode
      mv one/* two one/* two files moved, same inodes
      rclone move -q --no-traverse one two one two files moved, same inodes
      cp -r one two && rm -rf one one two/one new subfolder
      cp -r one/. two && rm -rf one one/. two
      cp -r one/* two && rm -rf one one/* two
      rsync -auh --remove-source-files one/ two one/ two
      rsync -auh --remove-source-files one two one two/one new subfolder
      1 vote
      1. kovboydan
        Link Parent
        Glad I could I inspire you. I always check the behavior of / before running rsync manually and always check the destination before I run dd. Too many bad experiences in my formative years from...

        Glad I could I inspire you.

        I always check the behavior of / before running rsync manually and always check the destination before I run dd. Too many bad experiences in my formative years from carelessness with those two in particular.

        3 votes
  3. [7]
    vord
    (edited )
    Link
    This one has a relatively simple reason, although pardon if my wording is bad. I have the mental model but am unsure if I've articulated clearly. I might have also reached a semi-correct...

    This one has a relatively simple reason, although pardon if my wording is bad. I have the mental model but am unsure if I've articulated clearly. I might have also reached a semi-correct conclusion with some weird incorrect jumps on the way. If anyone can correct me I'd be thrilled.

    # mv one/. three
    mv: cannot move 'one/.' to 'three/.': Device or resource busy
    

    one/. is a reference to the directory one, but from within the directory itself. So if you have a directory structure like this.

    one/a.txt
    one/b.txt
    two/one/
    

    You could "expand" the mv command sort of like this to get an idea of how that works:

    cd one
    mv ../one ../three
    mv: cannot move '../one' to a subdirectory of itself, 'three'
    

    This is a bit weird likely because .. is a hardlink (like a file) to the parent directory. But that is its own can of worms. Anyway...

    The reason you get the specific Device or resource busy when doing the original command is the same reason you get this (relies on having a device to mount):

    # mount /dev/zram0 ./one
    # cd one
    # umount ../one
    umount: /home/vord/tmp/one: target is busy.
    

    You're trying to remove the device while you're using the device.

    3 votes
    1. [6]
      kovboydan
      Link Parent
      Do you or @xk3 mind taking one for the team and trying a mv dir_1/. dir_2 when dir_2 is on a different file system. I’m curious as to whether the fallback in that situation for mv to be equivalent...

      Do you or @xk3 mind taking one for the team and trying a mv dir_1/. dir_2 when dir_2 is on a different file system.

      I’m curious as to whether the fallback in that situation for mv to be equivalent to a cp -a + rm will throw the same error.

      After skimming the source for coreutils mv - acknowledging I never messed with c and my c++ is 15 years rusty - I’m optimistic that it might work and would suggest the limitation is somewhere deeper like rename(). That’s just a guess though, really.

      Also on mac OS the error is “Invalid Argument”

      2 votes
      1. [4]
        vord
        (edited )
        Link Parent
        On OpenSUSE Tumbleweed: $ mv tmp/. /srv/storage mv: inter-device move failed: 'tmp/.' to '/srv/storage/.'; unable to remove target: Invalid argument (tmp is not /tmp)

        On OpenSUSE Tumbleweed:

        $ mv tmp/. /srv/storage
        mv: inter-device move failed: 'tmp/.' to '/srv/storage/.'; unable to remove target: Invalid argument
        

        (tmp is not /tmp)

        1 vote
        1. [3]
          kovboydan
          Link Parent
          Silly question but just to confirm: contents of tmp/ aren’t in /srv/storage?

          Silly question but just to confirm: contents of tmp/ aren’t in /srv/storage?

          1 vote
          1. [2]
            vord
            Link Parent
            Nope. I just had a blank file from a touch in tmp.

            Nope. I just had a blank file from a touch in tmp.

            1 vote
            1. kovboydan
              (edited )
              Link Parent
              Interesting. I know rm has exceptions / should fail for stuff like rm dir/. so maybe the behavior across file systems is the result of a clean up routine when the “rm“ fails. I don’t know but I’m...

              Interesting. I know rm has exceptions / should fail for stuff like rm dir/. so maybe the behavior across file systems is the result of a clean up routine when the “rm“ fails. I don’t know but I’m certainly curious about sorting it out.

              I mean…realpath, cp, and rsync have no problem with it and with mv the error differs between same file system and across file systems.

              Interesting, quite interesting.

              1 vote
      2. xk3
        Link Parent
        yeah I also get "Invalid argument" mv: inter-device move failed: 'test/.' to '/tmp/test/.'; unable to remove target: Invalid argument

        yeah I also get "Invalid argument"

        mv: inter-device move failed: 'test/.' to '/tmp/test/.'; unable to remove target: Invalid argument
        
        1 vote
  4. simplify
    Link
    My typical rsync options are -avz. The options are thoroughly explained here. Short explanation, Archive, Verbose, Compress. I find this to be a quick way of getting everything I want 99% of the...

    My typical rsync options are -avz. The options are thoroughly explained here. Short explanation, Archive, Verbose, Compress. I find this to be a quick way of getting everything I want 99% of the time. I also do a dry run -n first, so -avzn, just to make sure I didn't mess up my folders. Occasionally you have to apply other options, like --size-only, if your timestamps are a mess. Always happy to hear other opinions to better my workflow.

    2 votes