我可能知道如何使用readlink
符号链接获取普通文件。我有一个非常基本的想法来替换$@
传递给我的函数的任何符号链接,如下所示:
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f "$file")
done
示例是 SymLink /etc/os-release -> ../usr/lib/os-release
。
但如果我这样做:
set -- '/etc/os-release'; for file in "$@"; do [ -L "$file" ] && file=$(readlink -f "$file"); done; echo "$@"
我仍然得到原始的 SymLink 路径,这令人失望。我希望能够直接修改$@
,如果不可能的话,有什么解决办法吗?
答案1
你原来的代码:
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f -- "$file")
done
问题是file
循环中的设置对位置参数列表的内容没有影响。
相反,您可能想使用
for file in "$@"; do
[ -L "$file" ] && file=$(readlink -f -- "$file")
set -- "$@" "$file"
shift
done
现在,我们将从 的开头逐一移出条目"$@"
,并将可能修改的条目添加到列表的后面。$@
在for
循环中进行修改$@
不是问题,因为for
循环始终迭代静态列表,即迭代的列表仅在循环开始时评估一次。
上面的循环与下面的循环一样,在每次迭代中重置位置参数的整个列表。为一个巨大的文件名列表,这可能很快就会变得相当慢。如果你的 shell 有数组,斯蒂芬·基特的回答展示了一种加快速度的方法。
循环也可以写成稍微短一点的形式(调用readlink
每个元素),
for dummy do
set -- "$@" "$(readlink -f -- "$1")"
shift
done
这按照您的问题使用readlink
,即使这不是 POSIX 实用程序。
请注意,如果输出readlink
以一个或多个换行符结尾(除了添加的用于终止最后一行输出的内容之外),这些将被删除。这是命令替换的结果。如果这是一个问题,请在命令替换中人为地添加尾随字符,然后按照mosvy
建议将其删除在评论中:
for file do
[ -L "$file" ] && file=$(readlink -f -- "$file"; echo x)
set -- "$@" "${file%x}"
shift
done
答案2
请注意, 和 都不readlink
是realpath
POSIX 命令。 POSIX 命令行工具箱没有realpath()
类似的 API。在这里,您可以使用它,而不是readlink
(存在不同的不兼容实现),zsh
它也具有realpath()
内置的类似功能及其:P
修饰符。在你的sh
脚本中:
eval "set -- $(zsh -c 'print -r ${(qq)argv:P}' zsh "$@")"
zsh
尽管您也可以首先编写脚本,但它应该是:
argv=($argv:P)
或者是否python3
比 zsh 更可用:
eval "set -- $(
LC_ALL=C python3 -c '
import sys, os, shlex
print(" ".join(map(shlex.quote, map(os.path.realpath, sys.argv[1:]))))' "$@")"