¿Hay alguna manera de modificar la matriz $@ en un script de shell POSIX?

¿Hay alguna manera de modificar la matriz $@ en un script de shell POSIX?

Es posible que sepa cómo utilizarlo readlinkpara obtener archivos normales de SymLinks. Tuve esta idea muy básica de reemplazar cualquier SymLink $@pasado a mi función de la siguiente manera:

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

El ejemplo sería un SymLink /etc/os-release -> ../usr/lib/os-release.

Pero si hago esto:

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

Todavía obtengo la ruta SymLink original, lo cual es... decepcionante. Esperaba poder modificar directamente $@, si no es posible, ¿hay alguna solución?

Respuesta1

Tu código original:

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

El problema es que la configuración fileen el bucle no tiene consecuencias para el contenido de la lista de parámetros posicionales.

En su lugar, es posible que desee utilizar

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

Ahora vamos a eliminar las entradas del principio de "$@", una por una, y agregaremos las entradas posiblemente modificadas al final de la lista. Modificar $@en un forbucle $@no es un problema ya que forlos bucles siempre iteran sobre una lista estática, es decir, la lista sobre la que se itera se evalúa una vez al inicio del bucle, únicamente.

El bucle anterior, como los siguientes, restablece la lista completa de parámetros posicionales en cada iteración. Paraenormelista de nombres de archivos, esto pronto puede volverse bastante lento. Si su shell tiene matrices,La respuesta de Stephen Kitt.muestra una forma de acelerarlo.

El bucle también podría escribirse en una forma ligeramente más corta (que requiera readlinktodos y cada uno de los elementos),

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

Esto se utiliza readlinksegún su pregunta, aunque no sea una utilidad POSIX.

Tenga en cuenta que si la salida dereadlink termina con una o varias nuevas líneas(al lado del agregado para terminar la última línea de salida), estos se eliminarían. Esta es una consecuencia de la sustitución de mando. Si esto es un problema, agregue un carácter final artificialmente en la sustitución del comando y luego elimínelo, como mosvyse sugiere .en comentarios:

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

Respuesta2

Tenga en cuenta que ni readlinktampoco realpathlo son los comandos POSIX. El conjunto de herramientas de la línea de comandos POSIX no tiene realpath()una API similar. Aquí, en lugar de readlink(del cual existen diferentes implementaciones incompatibles), podría usar zshel que también tiene una realpath()funcionalidad similar incorporada con su :Pmodificador. En tu shguión:

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

Aunque también podrías escribir tu guión zshen primer lugar, donde sería simplemente:

argv=($argv:P)

O si python3es más probable que esté disponible 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:]))))' "$@")"

información relacionada