15

I'm trying to use find to create a bunch of symlinks but using the result with {} includes ./ before each filename. How can I avoid that?

find . -type l -name '*.h' -exec ln -s /sourcedir/{} /destinationdir/{} \;
6
  • Is your find supporting the -printf option?
    – manatwork
    Commented Apr 12, 2013 at 11:35
  • yes i think it does Commented Apr 12, 2013 at 11:38
  • See the find . -type l -name '*.h' -printf 'ln -s /sourcedir/%f /destinationdir/%f\n''s output. If you like it, pipe it to sh. Of course, special characters in the file names will be a problem.
    – manatwork
    Commented Apr 12, 2013 at 11:41
  • @manatwork I think you have the best answer there. If you post it as an answer I'll mark it as accepted Commented Apr 12, 2013 at 11:56
  • 1
    If all the relevant files (type l, name *.h) reside under /sourcedir/ then use find sourcedir -type l -name '*.h' -exec ln -s {} /destinationdir/{} \; ... You would run the find command from the parent of /sourcedir, which is the root. On second thought that won't work because it will try to create a link called /destinationdir/sourcedir/file.h
    – Johan
    Commented Apr 17, 2013 at 9:55

4 Answers 4

24

You only have to change one character in your command:

find * -type l -name '*.h' -exec ln -s /sourcedir/{} /destinationdir/{} \;
#    ^
4
  • 1
    Good point, but it should be noted though that it would fail if any file is named !, ( or ) or anything starting with a - in the current directory. It will also skip the hidden files and dirs in the current directory. Commented Apr 12, 2013 at 13:07
  • @StephaneChazelas You are right. I had thought about the hidden files and dirs myself but found only one such file in the 75K .h on my systems (ubuntu Linux).
    – Anthon
    Commented Apr 12, 2013 at 13:17
  • If I find stuff in another directory (eg. find src/ -type l), is there a way to exclude src/ from the results? Commented Apr 22, 2021 at 11:38
  • I don't understand why you would not do cd src; find * -type l in that case.
    – Anthon
    Commented Apr 22, 2021 at 17:08
7

Use the standard syntax, like:

S=/sourcedir D=/destdir find . -type l -name '*.h' -exec sh -c '
  for i do
    ln -s -- "$S${i#.}" "$D/$i"
  done' sh {} +

If you want to use GNUisms, you could do:

find . -type l -name '*.h' -printf '/sourcedir/%P\0/destdir/%P\0' |
  xargs -r0n2 ln -s

Or if /sourcedir is the current directory:

find "$PWD" -type l -name '*.h' -printf '%p\0/destdir/%P\0' |
  xargs -r0n2 ln -s
0

find will print names relative to the paths you provide as arguments. In this case, the path is ., so all the names will begin with ./. To get absolute paths, you need to provide an absolute path as input:

find "$PWD" -type l -name '*.h'

This command uses the $PWD environment variable, which contains the absolute path of the current working directory, so it should preserve the meaning of your original command.

2
  • 3
    He would then need to strip that $PWD off the destination of the ln command. Commented Apr 12, 2013 at 11:44
  • good point. Also I think I was answering a question he didn't ask. :( Commented Apr 12, 2013 at 11:45
0
find . -type l -name '*.h' -print0 | cut -z -c3- \
    | xargs -0 -I '{}' ln -s '/sourcedir/{}' '/destinationdir/{}'

man find

  • -print0 print the full file name on the standard output, followed by a null character.

man cut - remove sections from each line of files

  • -z line delimiter is NUL, not newline.
  • -c select only these characters.
  • N- from N'th byte, character or field, counted from 1, to end of line.

man xargs - build and execute command lines from standard input.

  • xargs [options] [command [initial-arguments]]
  • -0 Input items are terminated by a null character instead of by whitespace, and the quotes and backslash are not special (every character is taken literally).
  • -I replace-str Replace occurrences of replace-str in the initial-arguments with names read from standard input. Also, unquoted blanks do not terminate input items; instead the separator is the newline character. Implies -L 1.
  • -L max-lines Use at most max-lines nonblank input lines per command line.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .