Elimine las entradas duplicadas de $PATH con el comando awk

Elimine las entradas duplicadas de $PATH con el comando awk

Estoy intentando escribir una función de shell bash que me permita eliminar copias duplicadas de directorios de mi variable de entorno PATH.

Me dijeron que es posible lograr esto con un comando de una línea usando el awkcomando, pero no sé cómo hacerlo. ¿Alguien sabe cómo?

Respuesta1

Si aún no tiene duplicados en PATHy solo desea agregar directorios si aún no están allí, puede hacerlo fácilmente solo con el shell.

for x in /path/to/add …; do
  case ":$PATH:" in
    *":$x:"*) :;; # already there
    *) PATH="$x:$PATH";;
  esac
done

Y aquí hay un fragmento de shell que elimina duplicados de $PATH. Revisa las entradas una por una y copia las que aún no se han visto.

if [ -n "$PATH" ]; then
  old_PATH=$PATH:; PATH=
  while [ -n "$old_PATH" ]; do
    x=${old_PATH%%:*}       # the first remaining entry
    case $PATH: in
      *:"$x":*) ;;          # already there
      *) PATH=$PATH:$x;;    # not there yet
    esac
    old_PATH=${old_PATH#*:}
  done
  PATH=${PATH#:}
  unset old_PATH x
fi

Respuesta2

Aquí hay uninteligibleSolución de una sola línea que hace todo lo correcto: elimina duplicados, conserva el orden de las rutas y no agrega dos puntos al final. Por lo tanto, debería proporcionarle una RUTA deduplicada que ofrezca exactamente el mismo comportamiento que la original:

PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')"

Simplemente se divide en dos puntos ( split(/:/, $ENV{PATH})), utiliza usos grep { not $seen{$_}++ }para filtrar cualquier instancia repetida de rutas, excepto la primera aparición, y luego vuelve a unir las restantes separadas por dos puntos e imprime el resultado ( print join(":", ...)).

Si desea más estructura a su alrededor, así como la capacidad de deduplicar otras variables, pruebe este fragmento, que estoy usando actualmente en mi propia configuración:

# Deduplicate path variables
get_var () {
    eval 'printf "%s\n" "${'"$1"'}"'
}
set_var () {
    eval "$1=\"\$2\""
}
dedup_pathvar () {
    pathvar_name="$1"
    pathvar_value="$(get_var "$pathvar_name")"
    deduped_path="$(perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, $ARGV[0]))' "$pathvar_value")"
    set_var "$pathvar_name" "$deduped_path"
}
dedup_pathvar PATH
dedup_pathvar MANPATH

Ese código deduplicará tanto PATH como MANPATH, y puede invocar fácilmente dedup_pathvarotras variables que contienen listas de rutas separadas por dos puntos (por ejemplo, PYTHONPATH).

Respuesta3

Aquí hay uno elegante:

printf %s "$PATH" | awk -v RS=: -v ORS=: '!arr[$0]++'

Más largo (para ver cómo funciona):

printf %s "$PATH" | awk -v RS=: -v ORS=: '{ if (!arr[$0]++) { print $0 } }'

Ok, ya que eres nuevo en Linux, aquí te explicamos cómo configurar la RUTA sin un ":" final.

PATH=`printf %s "$PATH" | awk -v RS=: '{ if (!arr[$0]++) {printf("%s%s",!ln++?"":":",$0)}}'`

Por cierto, asegúrese de NO tener directorios que contengan ":" en su RUTA; de lo contrario, se estropeará.

algún crédito a:

Respuesta4

Siempre que agreguemos líneas que no sean awk:

PATH=$(zsh -fc "typeset -TU P=$PATH p; echo \$P")

(Podría ser tan simple como PATH=$(zsh -fc 'typeset -U path; echo $PATH')zsh siempre lee al menos un zshenvarchivo de configuración, que puede modificarse PATH).

Utiliza dos características interesantes de zsh:

  • escalares ligados a matrices ( typeset -T)
  • y matrices que eliminan automáticamente valores duplicados ( typeset -U).

información relacionada