Your original code:
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f -- "$file")
done
The issue is that setting file
in the loop has no consequence to the contents of the list of positional parameters.
Instead, you may want to use
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f -- "$file")
set -- "$@" "$file"
shift
done
Now we're shifting off the entries from the start of "$@"
, one by one, and adding the possibly modified entries to the back of the list. Modifying $@
in a for
loop over $@
is not an issue as for
loops always iterates over a static list, i.e. the list that is iterated over is evaluated once at the start of the loop, only.
The loop above, like the ones below, resets the entire list of positional parameters in each iteration. For a huge list of filenames, this may soon become quite slow. If your shell has arrays, Stephen Kitt's answer shows one way to speed it up.
The loop could also be written
in a slightly shorter form (calling readlink
for each and every element),
for dummy do
set -- "$@" "$(readlink -f -- "$1")"
shift
done
This uses readlink
as per your question, even though that's not a POSIX utility.
Note that if the output of readlink
ends with one or several newlines (beside the one added to terminate the last line of output), these would be stripped off. This is a consequence of the command substitution. If this is an issue, add a trailing character artificially in the command substitution, and then remove it, as mosvy
suggests in comments:
for file do
[ -L "$file" ] && file=$(readlink -f -- "$file"; echo x)
set -- "$@" "${file%x}"
shift
done