0

Why when I do this (I know, it's stupid) it returns the full path, not only the filename as expected? ls -l | awk '{print $9}' | xargs -I% find ./my_dir -type f -name "%" -exec echo $(basename {}) \;

And, yes, I know, it's stupid but the goal was to cp basename{} to {} (and, in the and, I use a loop for i in $(ls); do find ./my_dir -type f -name $i -exec cp $i {} \;; done)

Thanks.

Alysko
  • 103
  • 2
  • Because the shell replaces `$(basename {})` with `{}` before `xargs` starts. There's probably a duplicate question somewhere. I know [one on Super User](https://superuser.com/q/1720752/432690). – Kamil Maciorowski Aug 02 '22 at 13:21
  • 2
    Never do `for i in $(ls)`. It is [bad practice](http://mywiki.wooledge.org/BashPitfalls#for_f_in_.24.28ls_.2A.mp3.29), likely to break, and is harder than the simpler and safer `for i in *`. – terdon Aug 02 '22 at 13:50

1 Answers1

4

Your code will fail for any filename that contains a space (e.g this one.txt) or on any system where the user name or group name contains spaces (a system joined to Active Directory, for example).

The construct for i in $(ls) should not be used. Instead, use a wildcard glob, for i in *.

Finally, in direct answer to your question about $(basename {}). The $( ... ) value is not protected from the shell with single-quotes, and so it is evaluated before the command is executed. The result of $(basename {}) is simply {} and therefore,

xargs -I% find ./my_dir -type f -name "%" -exec echo $(basename {}) \;

is parsed and executed as if you had typed this,

xargs -I% find ./my_dir -type f -name % -exec echo {} \;

You were probably looking for something like this

for i in *
do
    find ./my_dir -type f -name "$i" -exec cp "$i" {} \; -quit
done

Don't forget that you should always double-quote your $ variables and expressions when you use them. (To be fair, it wouldn't have helped in this case, but it's something you need to get used to doing.)

roaima
  • 107,089
  • 14
  • 139
  • 261
  • "The $( ... ) value is not quoted, and so it is evaluated before the command is executed." -- it'd be evaluated (and `basename` would run) before `find` starts even with quoting. – ilkkachu Aug 03 '22 at 19:51
  • well, yes, with single quotes the shell wouldn't run it but before starting `find`, but even then `find` wouldn't expand it either. Because, well, it's just not something it does by itself, hence the `find -exec sh -c '...' sh {} \;` idiom – ilkkachu Aug 03 '22 at 20:17