16

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? Commented Apr 12, 2013 at 11:35
  • yes i think it does Commented Apr 12, 2013 at 11:38
  • 1
    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. Commented Apr 12, 2013 at 11:41
  • 1
    @manatwork, ITYW %P instead of %f here. Commented Apr 12, 2013 at 12:16
  • 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 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). 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. Commented Apr 22, 2021 at 17:08
8

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
1

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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.