I get error printouts when I use methods of (1) xargs find | xargs mv
and (2) xargs find -exec mv
to move files, but the moving of the files works as expected. I get no error printouts when I use (3) an intermediate variable, which is what I want.
Finding the inode numbers, which is what I'm doing with stat
and sed
, shouldn't be related to the problem.
I have condensed my problem into the following. Basically I want to get rid of the error printouts in both of Methods 1 and 2 (no piping to /dev/null please).
Note I'm on macOS, using BSD stat
, and I'm also using zsh
.
And I also know about find -print0 | xargs -0
, I just wanted to condense the problem so that it's not as long. This is as condensed as I want to get.
Setup
Make test subdirectories (which are later moved by inode number).
mkdir -p "./testdir/dir"{1..4}
Method 1 - xargs find | xargs mv
Works to move the subdirectories, but with error printouts.
stat -s ./testdir/* | sed -E -n 's/.*st_ino=([0-9] ).*/\1/p' | xargs -I abc find ./testdir/* -maxdepth 1 -inum abc | xargs -I {} mv {} ~/.Trash
Error printouts:
find: ./testdir/dir1: No such file or directory
find: ./testdir/dir1: No such file or directory
find: ./testdir/dir2: No such file or directory
find: ./testdir/dir1: No such file or directory
find: ./testdir/dir2: No such file or directory
find: ./testdir/dir3: No such file or directory
Method 2 - xargs find -exec mv
Works to move the subdirectories, but with error printouts.
stat -s ./testdir/* | sed -E -n 's/.*st_ino=([0-9] ).*/\1/p' | xargs -I abc find ./testdir/* -maxdepth 1 -inum abc -exec mv {} ~/.Trash \;
Error printouts:
find: ./testdir/dir1: No such file or directory
find: ./testdir/dir1: No such file or directory
find: ./testdir/dir2: No such file or directory
find: ./testdir/dir1: No such file or directory
find: ./testdir/dir2: No such file or directory
find: ./testdir/dir3: No such file or directory
find: ./testdir/dir1: No such file or directory
find: ./testdir/dir2: No such file or directory
find: ./testdir/dir3: No such file or directory
find: ./testdir/dir4: No such file or directory
Method 3 - intermediate variable
Works to move the subdirectories, but this time without error printouts, which is what I want.
dirs_to_move=$(stat -s ./testdir/* | sed -E -n 's/.*st_ino=([0-9] ).*/\1/p' | xargs -I abc find ./testdir/* -maxdepth 1 -inum abc)
printf '%s\n' "$dirs_to_move" | xargs -I {} mv {} ~/.Trash
Question
How do I eliminate the error printouts from both of Methods 1 and 2? What is causing the printouts?
CodePudding user response:
Short answer: find
is being told to search all 4 subdirectories, even after some of them have been deleted.
Detailed eplanation: The root problem is that the wildcard in the xargs -I abc find ./testdir/* -maxdepth 1 ...
part gets expanded by the shell before any of the commands get run. So that part of the script becomes:
... | xargs -I abc find ./testdir/dir1 ./testdir/dir2 ./testdir/dir3 ./testdir/dir4 -maxdepth 1 ...
What happens then is the stat | sed
part sends the first inode number to xargs
, that runs find
, find
searches all 4 directories for the matching item, and either deletes it directly or sends its path to the next xargs
for deletion.
Next, the stat | sed
part sends the second inode number to xargs
, that runs find
, find
searches all 4 directories... oops, hey, one of them's missing! So it prints an error message about ./testdir/dir1 not existing, searches the other three, and (one way or another) deletes the next one.
Next comes the third inode number, and this time neither ./testdir/dir1 nor ./testdir/dir2 exists, so you get two error messages. Etc etc etc.
(There's also an additional problem with the second one, where immediately after running the mv
command, find
then tries to search its contents, and oops it's gone. That's why you get more error messages that way. I think you might've wanted -maxdepth 0
to keep it from trying to do that.)
Solution: I'm not sure what the larger context is, but my immediate reaction is that this is an overcomplex mess and as much as possible should be removed. But without knowing what can be changed without breaking the big picture, the minimal fix I see is to just have find
search the entire testdir directory, rather than using a wildcard to list specific (wrong) subdirectories:
... | xargs -I abc find ./testdir -maxdepth 1 ...
(And note that in this form, -maxdepth 1
is actually correct.)