Home > Net >  bash: redirect output of "find -exec" into a file without newlines; -- temporarily removin
bash: redirect output of "find -exec" into a file without newlines; -- temporarily removin

Time:09-16

Sometimes, I use VirtualBox with a Windows guest system under a Linux Mint host system, with a shared directory ~/_D corresponding to my D:\-drive in Windows. However, there are many symbolic links to directories below ~/_D, some of which are even circular, which causes some Windows processes not to work as I need.

Therefore I am working on a bash script that temporarily removes all symlinks below ~/_D and at the same time creates another script that can restore those symlinks later when I am done with my work on Windows.

However, I am struggling with writing the output of the find ... -exec readlink {} \; command into a file without newlines. My script currently goes like this:

#! /bin/bash

cd ~/_D

echo "# restores all symlinks deleted by ~/bin/symlink.remove" > symlinks.restore
echo >> symlinks.restore

find . -type l -exec readlink {} \; -print -exec echo "" \; -exec rm {} \; >> symlinks.restore

echo >> symlinks.restore
echo "# run emacs command sd-restore-symlinks on this file" >> symlinks.restore

chmod u x symlinks.restore

echo to restore symlinks, run emacs command sd-restore-symlinks on ~/_D/symlinks.restore
echo then run ~/_D/symlinks.restore

Instead of awkwardly using an emacs search-and-replace-function just for rearranging the output for each symlink from two lines, separated by an empty line, into one line, resulting in lines such as ln -s "../.Events.OLD" "./Events/Events.OLD" (mind the quotes to avoid havoc with possible spaces in filenames), I would love to generate the output file symlinks.restore directly from my bash script.

However, using hacks like: find -type l -exec echo -n $(readlink {}) \; does not work, probably because{} is not accessible as alias for the current file within $(), also not when quoted \{\}.

I guess that some smart combination of -printf or -print0 actions of find, and/or perhaps sed, can do the trick, but this goes beyond what I can achieve.

Also, perhaps there are already tools to temporarily remove and then restore all symlinks below a certain directory?

Any ideas?

CodePudding user response:

You may use this find command to create restore script:

find . -type l -exec bash -c '
    for symlnk; do
        printf "ln -s %q %q\n" "$(readlink "$symlnk")" "$symlnk"
    done' _ {}   > symlinks.restore

The restore script (symlinks.restore) must be run (or sourced) from the same directory as the find command is run.

CodePudding user response:

With -printf something like.

find . -type l -printf 'ln -s "%p" "%l"\n' > symlinks.restore

It has limitations and it is not bullet proof, which was mentioned by M. Nejat Aydin

CodePudding user response:

Thanks to both, M. Nejat Aydin and Jetchisel.

The latter solution worked best for me, with some adjustments (quotes, changed order of parameters %p and %l, and added rm command).

My script now reads:

#! /bin/bash

cd ~/_D

printf "#! /bin/bash\n\n# restores all symlinks deleted by ~/bin/symlink.remove\n# has to be executed in _D\n\n" > symlinks.restore

find . -type l -printf 'ln -s "%l" "%p"\n' -exec rm {} \; >> symlinks.restore

chmod u x symlinks.restore

This works fine, even with spaces in filenames.

Differently from my earlier comments, the other solution also works without producing literal backslashes in the link targets; the relevant command I now successfully used is

find . -type l -exec bash -c '
    for symlnk; do
        printf "ln -s \"%q\" \"%q\"\n" "$(readlink "$symlnk")" "$symlnk"
    rm $symlnk
    done' _ {}   >> symlinks.restore

Thanks again to all who responded.

  • Related