有没有办法在 POSIX shell 脚本中修改 $@ 数组

有没有办法在 POSIX shell 脚本中修改 $@ 数组

我可能知道如何使用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

请注意, 和 都不readlinkrealpathPOSIX 命令。 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:]))))' "$@")"

相关内容