Existe uma maneira de modificar o array $@ em um script de shell POSIX

Existe uma maneira de modificar o array $@ em um script de shell POSIX

Talvez eu saiba como readlinkobter arquivos normais do SymLinks. Eu tive essa ideia básica de substituir qualquer SymLink $@passado para minha função da seguinte maneira:

for file in "$@"; do
    [ -L "$file" ] && file=$(readlink -f "$file")
done

O exemplo seria um SymLink /etc/os-release -> ../usr/lib/os-release.

Mas se eu fizer isso:

set -- '/etc/os-release'; for file in "$@"; do [ -L "$file" ] && file=$(readlink -f "$file"); done; echo "$@"

Ainda consigo o caminho SymLink original, o que é... decepcionante. Eu esperava poder modificar diretamente $@, se não for possível, existe alguma solução alternativa?

Responder1

Seu código original:

for file in "$@"; do
    [ -L "$file" ] && file=$(readlink -f -- "$file")
done

A questão é que a configuração fileno loop não tem consequências para o conteúdo da lista de parâmetros posicionais.

Em vez disso, você pode querer usar

for file in "$@"; do
    [ -L "$file" ] && file=$(readlink -f -- "$file")
    set -- "$@" "$file"
    shift
done

Agora estamos deslocando as entradas do início de "$@", uma por uma, e adicionando as entradas possivelmente modificadas ao final da lista. Modificar $@em um forloop $@não é um problema, pois foros loops sempre iteram em uma lista estática, ou seja, a lista que é iterada é avaliada apenas uma vez no início do loop.

O loop acima, como os abaixo, redefine toda a lista de parâmetros posicionais em cada iteração. Paraenormelista de nomes de arquivos, isso poderá se tornar bastante lento em breve. Se o seu shell tiver matrizes,A resposta de Stephen Kittmostra uma maneira de acelerar isso.

O loop também pode ser escrito em uma forma um pouco mais curta (chamando readlinkcada elemento),

for dummy do
    set -- "$@" "$(readlink -f -- "$1")"
    shift
done

Isso é usado readlinkde acordo com sua pergunta, mesmo que não seja um utilitário POSIX.

Observe que se a saída dereadlink termina com uma ou várias novas linhas(ao lado daquele adicionado para encerrar a última linha de saída), eles seriam removidos. Esta é uma consequência da substituição do comando. Se isso for um problema, adicione um caractere final artificialmente na substituição do comando e remova-o, como mosvysugerenos comentários:

for file do
    [ -L "$file" ] && file=$(readlink -f -- "$file"; echo x)
    set -- "$@" "${file%x}"
    shift
done

Responder2

Observe que nem readlinkos realpathcomandos POSIX são. O conjunto de ferramentas de linha de comando POSIX não possui realpath()API semelhante. Aqui, em vez de readlink(dos quais existem diferentes implementações incompatíveis), você pode usar zshwhich também possui uma realpath()funcionalidade semelhante a -integrada com seu :Pmodificador. No seu shroteiro:

eval "set -- $(zsh -c 'print -r ${(qq)argv:P}' zsh "$@")"

Embora você também possa escrever seu script em zshprimeiro lugar, onde seria apenas:

argv=($argv:P)

Ou se python3é mais provável que esteja disponível que zsh:

eval "set -- $(
  LC_ALL=C python3 -c '
import sys, os, shlex
print(" ".join(map(shlex.quote, map(os.path.realpath, sys.argv[1:]))))' "$@")"

informação relacionada