0

on mac using gnu tools

Im not sure whats happening but it seems piping the output of find to xargs has some issue with touching files with the same name that appear in the directory structure in different places

my directory:

dir
-- dirone
---- myfile.txt
---- otherfile.txt
-- dirtwo
---- myfile.txt

When I run this command I get a permissions denied error:

find dir -name "myfile.txt" | xargs -0 -I FILE sh -c "ls FILE"

I cant even touch/ls the file. If I access "otherfile.txt" of which there is one I see no issues

find dir -name "otherfile.txt" | xargs -0 -I FILE sh -c "ls FILE"

Is there some kind of race condition or something? I eventually want to modify the files with sed -i but I cant even ls them. I dont understand because the full file path is being passed to xargs so it should have the complete path making the fact that its the same name not matter right?

EDIT: OK still dont understand why I was getting perms errors but this seems to work:

find dir -name "otherfile.txt" -type file -print0 | xargs -0 -I FILE sh -c "ls FILE"

Just removing -0 also worked, I'm not sure which way is better/safer?

user1028270
  • 1,044

1 Answers1

3

Given your directory setup:

$ find dir
dir
dir/dirtwo
dir/dirtwo/myfile.txt
dir/dirone
dir/dirone/myfile.txt
dir/dirone/otherfile.txt

If I use your (original) command, then I can reproduce your original problem:

$ find dir -name "myfile.txt" | xargs -0 -I FILE sh -c "ls FILE"
dir/dirtwo/myfile.txt
sh: line 1: dir/dirone/myfile.txt: Permission denied
$

To better understand what's happening, let's try a variation of that command:

$ find dir -name "myfile.txt" | xargs -0 -I FILE echo -FILE-
-dir/dirtwo/myfile.txt
dir/dirone/myfile.txt
-

Note that FILE expanded to a single token: dir/dirtwo/myfile.txt\ndir/dirone/myfile.txt\n. Why? Because you told xargs that tokens were NUL-delimited (using the -0 switch), and there was no NUL character between the two filenames.

With that, if we turn back to your original command: ... | xargs -0 -I FILE sh -c "ls FILE", that turns into:

ls dir/dirtwo/myfile.txt
dir/dirone/myfile.txt

So it lists one, and tries to run the second. That's why you get "Permission denied" -- the second file isn't executable.

As you've found, adding the -print0 option to find will resolve the problem. With that option, find will put a NUL character between the token -- the same character xargs expect when you use the -0 option. Similarly, just leaving the -0 option off of xargs solves the problem because both find and xargs use whitespace as delimiters by default.

The -print0 and -0 options are useful for cases where filenames can contain spaces (which breaks the default behavior where spaces are considered token delimiters).

Andy Dalton
  • 13,993