Search code examples
bashfindxargsstatmv

"mv: cannot stat" error when using find to move files via xargs


I'm working on a bash script that needs to (recursively) move all files and folders within a source folder into a destination folder.

In trying to make this as robust as possible, and address potential argument list too long - errors, I opted to use the find command (to safely determine the files to move) piped into xargs (to efficiently group the moves together). I'm also using -print0 and -0 to address potential problems with spaces.

I've written the following test script:

#!/bin/bash

# create test source file structure, and destination folder
mkdir -p sourceDir/{subdir1,subdir\ with\ space\ 2}
mkdir -p destDir
touch sourceDir/subdir1/{a1.txt,noExtension1,file1\ with\ space.txt}
touch sourceDir/subdir\ with\ space\ 2/{a2.txt,noExtension2,file2\ with\ space.txt}

#move files/folders from source to destination
find sourceDir -mindepth 1 -print0 | xargs -0 mv --target-directory=destDir

Which seems to work (i.e. the files are moved) but for some reason I get numerous errors as follows:

mv: cannot stat `sourceDir/subdir with space 2/file2 with space.txt': No such file or directory
mv: cannot stat `sourceDir/subdir with space 2/noExtension2': No such file or directory
mv: cannot stat `sourceDir/subdir with space 2/a2.txt': No such file or directory
mv: cannot stat `sourceDir/subdir1/file1 with space.txt': No such file or directory
mv: cannot stat `sourceDir/subdir1/noExtension1': No such file or directory
mv: cannot stat `sourceDir/subdir1/a1.txt': No such file or directory

Should there not be a way to do this (for the source files indicated in my script) without generating errors?

Why are these errors being generated (since the files and folders are in fact being moved)?


Solution

  • I've sussed it.

    Short answer: I was missing the -maxdepth 1 tag. The following command works

    find sourceDir -mindepth 1 -maxdepth 1 -print0 | xargs -0 mv --target-directory=destDir
    # as does
    find sourceDir -mindepth 1 -maxdepth 1 -exec mv --target-directory=destDir '{}' +
    

    Longer answer:

    My original find command was listing the paths to each and every file, and then trying to move each and every one of them. Once a top level folder had been moved, there was then no need to move any files/folders underneath - but the script was still trying to!

    The script below demonstrates this (without actually performing the moves):

    #!/bin/bash
    
    mkdir -p sourceDir/{subdir1/subsubdir1,subdir\ with\ space\ 2}
    touch sourceDir/subdir1/{subsubdir1/deeperFile.log,a1.txt,noExtension1,file1\ with\ space.txt}
    touch sourceDir/subdir\ with\ space\ 2/{a2.txt,noExtension2,file2\ with\ space.txt}
    touch sourceDir/rootFile.txt
    
    echo -e "\n--- Output of 'ls -1: sourceDir':"
    echo "$(ls -1 sourceDir)"
    
    echo -e "\n--- original incorrect attempt was trying to move each of the following:"
    find sourceDir -mindepth 1
    
    echo -e "\n--- working attempt only needs to move each of the top level files/folders, everything underneath any folders will get moved automatically"
    find sourceDir -mindepth 1 -maxdepth 1
    

    giving output:

    --- Output of 'ls -1: sourceDir':
    rootFile.txt
    subdir1
    subdir with space 2
    
    --- original incorrect attempt was trying to move each of the following:
    sourceDir/rootFile.txt
    sourceDir/subdir with space 2
    sourceDir/subdir with space 2/file2 with space.txt
    sourceDir/subdir with space 2/noExtension2
    sourceDir/subdir with space 2/a2.txt
    sourceDir/subdir1
    sourceDir/subdir1/file1 with space.txt
    sourceDir/subdir1/noExtension1
    sourceDir/subdir1/a1.txt
    sourceDir/subdir1/subsubdir1
    sourceDir/subdir1/subsubdir1/deeperFile.log
    
    --- working attempt only needs to move each of the top level files/folders, everything     underneath any folders will get moved automatically
    sourceDir/rootFile.txt
    sourceDir/subdir with space 2
    sourceDir/subdir1