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: