replace all symlinks with original

Ken Hirakawa picture Ken Hirakawa · Aug 23, 2011 · Viewed 20.8k times · Source

I have the following directory structure

/symdir
  sym1 -> ../dir1
  sym2 -> ../dir2
  hello.txt

And then

/dir1
  some
  files
  here
/dir2
  more
  files

I would like to replace the symlinks in symdir (sym1, sym2) with the originals. I.e.

some_awesome_bash_func symdir symdir_output

Would create

/symdir_output
  /dir1
    some
    files
    here
  /dir2
    more
    files
  hello.txt

How would I accomplish this?

Answer

Julien Palard picture Julien Palard · Oct 1, 2012

My very personal trick for files (not directories):

sed -i '' **/*

Note that I'm using ** which uses the bash globstar option, you may have to enable it beforehand:

shopt -s globstar

How it works

I trick sed to do the job, by using an implementation detail of the sed inplace mode.

sed is a tool to edit streams of text. The -i option of sed means inplace, the empty string '' is the instruction set: so there's no instruction, sed will do nothing. **/* is a bash globstar pattern meaning "all files and all folders, at all depth, from here".

The algorithm sed uses to edit a file inplace is:

  • Create a temporary file as the output file,
  • for each line in the input file:
    • apply the transformation, write to the output file.
  • Move the output file over the input file.

As I'm asking no transformations (the empty string), the algorithm can be simplified as:

  • Create a temporary file,
  • copy the content of the original file to the temporary file
  • move the temporary file over the original file.

The temporary file is a real file, sed completly ignores that the input file was a symlink, it just reads it. So at the last step, when sed moves the temporary file over the real file, it "overwrite" the symlink with a real file, that's what we wanted.

This also explains why it won't work to transform a "symlink to a directory" to a real directory: sed works on file contents.