0

I'm trying to write a simple command that will create symlinks for all files in a specific directory (Yes, exactly like lndir but I cannot use it so I try to mimic it).

I tried the following using find + xargs:

find $(realpath ${FROM_DIR}) -maxdepth 1 -print0 | xargs -0 -I{} ln -sfn {} ${TO_DIR}/$(basename {})

But the problem is that the shell substitutes $(basename {}) once before the line is executed, and not for every argument.

Writing it as a loop looks like this:

for i in $(find $(realpath ${FROM_DIR}) -maxdepth 1)
do
    ln -sfn $i ${TO_DIR}/$(basename $i)
done

This works, but it is relatively slow. I was wondering if this can be done using xargs as well.

1 Answers1

3

Sounds like you want:

ln -sf -- "$(realpath -- "$FROM_DIR")"/* "$TO_DIR/"

When the last argument is a directory, ln, like cp or mv creates links to the other argument with same basename inside that directory.

Or with zsh:

ln -sf -- $FROM_DIR:P/* $TO_DIR/

Which would also behave better if the real path of $FROM_DIR cannot be determined or no file can be found in $FROM_DIR.

(add the D qualifier to also create symlinks for the hidden files).

Or if you really needed to call one ln invocation per file for some reason:

for f ($FROM_DIR:P/*(ND)) ln -sfT -- $f $TO_DIR/$f:t

(note that -n and -T are non-standard extensions).

To use shell command substitution with xargs (or here, with find -exec, using xargs would be pointless) you'd need them to run a shell:

TO_DIR=$TO_DIR find "$(realpath -- "$FROM_DIR")" -mindepth 1 -maxdepth 1 \
  -exec sh -c '
    for file do
      ln -sfn -- "$file" "$TO_DIR/$(basename -- "$file")"
    done' sh {} +

But bear in mind that basename can't be used in command substitution for arbitrary files as command substitution strips all trailing newline characters (there's the same problem with $(realpath) here). You'd better use sh's ${file##*/} or zsh $file:t here ($FROM_DIR:P for realpath).

Here, you only need:

find "$(realpath -- "$FROM_DIR")" -mindepth 1 -maxdepth 1 \
  -exec ln -sft "$TO_DIR" -- {} +

though (-t being also a GNU extension). That one is not affected by the too many arguments errors you could get if there are too many files in $TO_DIR (in zsh, you'd work around it by using zargs or by loading the zsh/files module to get a builtin version of ln).

See also these Q&A's for other problems with your attempts: