¿Una forma más cómoda de editar un $ PATH largo?

35

Quiero agregar, en ~ / .bashrc, algunos directorios a mi $ PATH.

Mi $ PATH es bastante largo, por lo que es un poco difícil ver qué directorios contiene y en qué orden.

Sé que puedo modificar mi ~ / .bashrc para que sea:

PATH=$PATH:/some/dir
PATH=$PATH:/another/dir:/yet/another
PATH=$PATH:/and/another
...

facilitaría la lectura. Pero me preguntaba si durante los últimos años Bash ha adquirido alguna sintaxis que facilita la especificación de una RUTA larga. Por ejemplo, fantaseo con una sintaxis similar a:

PATH=:((
  /some/dir
  /another/dir
  /yet/another
  /and/another
  ...
))

Yo como la sintaxis no es válida. Me preguntaba si hay algo tan fácil. ¿Esta ahí?

Niccolo M.
fuente
El tutorial tradicional para establecer el camino PATH=foo:$PATHparece incorrecto porque mantiene el crecimiento todo el tiempo source ~/.bashrce incluso exec bashno puede ayudar desde el $PATHprincipio export.
林果 皞

Respuestas:

25

Utilizo un conjunto de funciones de conveniencia para anteponer o agregar una ruta a una variable. Las funciones vienen en el tarball de distribución para Bash en un archivo contrib llamado "pathfuncs".

  • add_path agregará la entrada al final de la variable PATH
  • pre_path agregará la entrada al comienzo de la variable PATH
  • del_path eliminará la entrada de la variable PATH, donde sea que esté

Si especifica una variable como segundo argumento, la usará en lugar de PATH.

Por conveniencia, aquí están:

# is $1 missing from $2 (or PATH) ?
no_path() {
    eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
  no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
    sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}

Si los agrega a su archivo de inicio de bash, puede agregarlos a su RUTA de esta manera:

pre_path $HOME/bin
add_path /sbin
add_path /usr/sbin

O especifique una variable diferente:

pre_path $HOME/man MANPATH
pre_path $HOME/share/man MANPATH
add_path /usr/local/man MANPATH
add_path /usr/share/man MANPATH

Utilizo este método en mis archivos rc poniendo primero las pre_paths y las add_paths en segundo lugar. Hace que todos mis cambios de ruta sean fáciles de entender de un vistazo. Otro beneficio es que las líneas son lo suficientemente cortas como para que pueda agregar un comentario final en una línea si es necesario.

Y dado que estas son funciones, puede usarlas de forma interactiva desde la línea de comandos, como decir add_path $(pwd)que agregue el directorio actual a la ruta.

Estrella de mar
fuente
Gracias. Revisé tu código y funciona. Sorprendentemente, también encontré un uso para del_path (A "." Se arrastra en mi RUTA en algunas situaciones, el diablo sabe de dónde, así que lo hice del_path .).
Niccolo M.
Hola. ¿Es posible obtener (incluir) este pathfuncs del script bashrc o debería copiarlo / pegarlo allí?
Cristiano
@ Cristiano Cualquiera de los dos funcionará. Realmente depende de ti.
Estrella de
11

Bien, descubrí la siguiente solución, que creo que es elegante (en lo que respecta a la sintaxis de shell). Utiliza la sintaxis de matriz de Bash y también una forma ordenada de unir elementos:

paths=(
  /some/dir
  /another/dir
  '/another/dir with spaces in it'
  /yet/another
  /and/another
  /end
)
paths_joined=$( IFS=: ; echo "${paths[*]}" )

PATH=$paths_joined:$PATH

¡ALERTA!

Resulta que esta solución tiene un problema : a diferencia de las soluciones de @terdon y @Starfish, no comprueba primero si las rutas ya están en RUTA. Entonces, dado que quiero poner este código en ~ / .bashrc (y no en ~ / .profile), las rutas duplicadas se arrastrarán en PATH. Por lo tanto, no use esta solución (a menos que la ponga en ~ / .profile (o, mejor, ~ / .bash_profile ya que tiene una sintaxis específica de Bash)).

Niccolo M.
fuente
1
Muy agradable. ¿Puede aceptar una respuesta para que otros no vengan a ofrecer una solución cuando ya la haya encontrado? Gracias
Básico
Las rutas duplicadas no son realmente un problema. Es muy poco probable que haya agregado suficientes directorios para PATHcausar problemas de rendimiento (especialmente porque el shell almacena en caché las búsquedas exitosas).
chepner
5

Yo uso la siguiente función en mi ~/.bashrc. Es algo que obtuve de un administrador de sistemas en mi antiguo laboratorio, pero no creo que los haya escrito. Simplemente agregue estas líneas a su ~/.profileo ~/.bashrc:

pathmunge () {
        if ! echo $PATH | /bin/grep -Eq "(^|:)$1($|:)" ; then
           if [ "$2" = "after" ] ; then
              PATH=$PATH:$1
           else
              PATH=$1:$PATH
           fi
        fi
}

Esto tiene varias ventajas:

  • La adición de nuevos directorios a la $PATHes trivial: pathmunge /foo/bar;
  • Evita entradas duplicadas;
  • Puede elegir si desea agregar una nueva entrada al principio ( pathmunge /foo/baro al final ( pathmunge /foo/bardespués) del $PATH.

El archivo de inicialización de su shell contendría algo como:

pathmunge /some/dir
pathmunge /another/dir
pathmunge '/another/dir with spaces in it'
pathmunge /yet/another
pathmunge /and/another
pathmunge /end
terdon
fuente
Gracias. Pero elegiré la solución de @ Starfish porque la suya no genera grep.
Niccolo M.
2
@NiccoloM. no hay problema, acepta el que prefieras. Sin embargo, tenga cuidado con el enfoque de starfish, ejecutará código arbitrario a través de él evalpara que pueda causar daños graves si lo ejecuta con el argumento incorrecto.
terdon
Tenga en cuenta que existe la función más rápido en RedHat de hacerlo sin orden externa grep, ver bugzilla.redhat.com/show_bug.cgi?id=544652#c7
林果皞
4

Quiero agregar, en ~ / .bashrc, algunos directorios a mi $ PATH.

Yo uso lo siguiente en Cygwin. Debería funcionar en otras versiones de bash. Puede eliminar el unset PATHpara construir en su actual PATH(si lo hace, puede que tenga que descubrir cómo agregar los :separadores correctamente).

Nota:

  • Una vez tuve esta funcionalidad en una bashfunción, pero la perdí después de un bloqueo de disco.

En mi .bash_profile:

# Build up the path using the directories in ~/.path_elements
unset PATH
while read line; do 
  PATH="${PATH}$line"; 
done < ~/.path_elements

...

# Add current directory to path
export PATH=".:${PATH}"

En ~/.path_elements:

/home/DavidPostill/bin:
/usr/local/bin:
/usr/bin:
/c/Windows/system32:
/c/Windows
DavidPostill
fuente
Gracias. Su respuesta me inspiró a trabajar en una solución similar. (Para mi gusto, guardar las rutas en un archivo separado es una molestia).
Niccolo M.
1

Lo uso en mi .bashrc (y también en mi .zshrc, ya que normalmente uso zsh donde está disponible en lugar de bash). De acuerdo, requiere que agregue directorios manualmente, pero una ventaja es que a medida que lo actualizo, puedo seguir copiándolo en nuevos servidores y no preocuparme de que la RUTA en un nuevo servidor se cree con directorios que no existen allí.

##
## CAMINO
##
## En lugar de simplemente golpear nuestra RUTA con directorios que pueden
## no es apropiado para este servidor, trata de ser inteligente sobre lo que agregamos
##
RUTA = / usr / local / sbin: / usr / local / bin: / usr / sbin: / usr / bin: / sbin: / bin
[-d / cs / sbin] && PATH = / cs / sbin: $ PATH
[-d / cs / bin] && PATH = / cs / bin: $ PATH
[-d / usr / ucb] && PATH = $ PATH: / usr / ucb
[-d / usr / ccs / bin] && PATH = $ PATH: / usr / ccs / bin
[-d / usr / local / ssl / bin] && PATH = $ PATH: / usr / local / ssl / bin
[-d / usr / krb5 / bin] && PATH = $ PATH: / usr / krb5 / bin
[-d / usr / krb5 / sbin] && PATH = $ PATH: / usr / krb5 / sbin
[-d / usr / kerberos / sbin] && PATH = $ PATH: / usr / kerberos / sbin
[-d / usr / kerberos / bin] && PATH = $ PATH: / usr / kerberos / bin
[-d /cs/local/jdk1.5.0/bin] && PATH = $ PATH: /cs/local/jdk1.5.0/bin
[-d /usr/java/jre1.5.0_02/bin] && PATH = $ PATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/bin] && PATH = $ PATH: /usr/java1.2/bin
[-d /cs/local/perl5.8.0/bin] && PATH = $ PATH: /cs/local/perl5.8.0/bin
[-d / usr / perl5 / bin] && PATH = $ PATH: / usr / perl5 / bin
[-d / usr / X11R6 / bin] && PATH = $ PATH: / usr / X11R6 / bin
[-d / etc / X11] && PATH = $ PATH: / etc / X11
[-d / opt / sfw / bin] && PATH = $ PATH: / opt / sfw / bin
[-d / usr / local / apache / bin] && PATH = $ PATH: / usr / local / apache / bin
[-d / usr / apache / bin] && PATH = $ PATH: / usr / apache / bin
[-d / cs / admin / bin] && PATH = $ PATH: / cs / admin / bin
[-d / usr / openwin / bin] && PATH = $ PATH: / usr / openwin / bin
[-d / usr / xpg4 / bin] && PATH = $ PATH: / usr / xpg4 / bin
[-d / usr / dt / bin] && PATH = $ PATH: / usr / dt / bin

Hago lo mismo para mi MANPATH:

##
## MANPATH
##
## En lugar de simplemente golpear nuestro MANPATH con directorios que pueden
## no es apropiado para este servidor, trata de ser inteligente sobre lo que agregamos
##
MANPATH = / usr / local / man
[-d / usr / share / man] && MANPATH = $ MANPATH: / usr / share / man
[-d / usr / local / share / man] && MANPATH = $ MANPATH: / usr / local / share / man
[-d / usr / man] && MANPATH = $ MANPATH: / usr / man
[-d / cs / man] && MANPATH = $ MANPATH: / cs / man
[-d / usr / krb5 / man] && MANPATH = $ MANPATH: / usr / krb5 / man
[-d / usr / kerberos / man] && MANPATH = $ MANPATH: / usr / kerberos / man
[-d / usr / local / ssl / man] && MANPATH = $ MANPATH: / usr / local / ssl / man
[-d /cs/local/jdk1.5.0/man] && MANPATH = $ MANPATH: /cs/local/jdk1.5.0/man
[-d /usr/java/jre1.5.0_02/man] && MANPATH = $ MANPATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/man] && MANPATH = $ MANPATH: /usr/java1.2/man
[-d / usr / X11R6 / man] && MANPATH = $ MANPATH: / usr / X11R6 / man
[-d / usr / local / apache / man] && MANPATH = $ MANPATH: / usr / local / apache / man
[-d / usr / local / mysql / man] && MANPATH = $ MANPATH: / usr / local / mysql / man
[-d /cs/local/perl5.8.0/man] && MANPATH = $ MANPATH: /cs/local/perl5.8.0/man
[-d / usr / perl5 / man] && MANPATH = $ MANPATH: / usr / perl5 / man
[-d / usr / local / perl / man] && MANPATH = $ MANPATH: / usr / local / perl / man
[-d /usr/local/perl5.8.0/man] && MANPATH = $ MANPATH: /usr/local/perl5.8.0/man
[-d / usr / openwin / man] && MANPATH = $ MANPATH: / usr / openwin / man

Además de tener un solo archivo que puedo copiar en sistemas en entornos dispares sin temor a agregar directorios no existentes a la RUTA, este enfoque también tiene la ventaja de permitirme especificar el orden en que quiero que aparezcan los directorios en la RUTA. Dado que la primera línea de cada definición redefine completamente la variable PATH, puedo actualizar mi .bashrc y obtenerlo después de la edición para actualizar mi shell sin agregar entradas duplicadas (que solía experimentar hace mucho tiempo cuando simplemente comencé con " $ PATH = $ PATH: / new / dir ". Esto garantiza que obtenga una copia limpia en el orden que deseo.

Brian Snook
fuente
1
sugiriendo una alternativa: d="/usr/share/man" ; [ -d "$d" ] && MANPATH="$MANPATH:${d}"será más corto y más fácil agregar un nuevo directorio (solo copie una línea y edite la primera parte "d = ...."). Sin embargo, para el PATH, creo que terminarás con demasiados directorios en tu PATH, lo que no siempre es bueno (¿qué pasa si existe un comando "foo" en uno de los caminos menos conocidos y haces algo completamente diferente? que lo que se esperaría de un usuario normal)?
Olivier Dulac
El OP solicitó una forma más concisa de agregar rutas. Esto es mucho más detallado y más propenso a errores que lo que ya estaban tratando de evitar.
underscore_d
-1

¡Hay una manera fácil! Lea las funciones de Shell y las variables de ruta en Linux Journal , 01 de marzo de 2000 Por Stephen Collyer

Las funciones me permiten usar un nuevo tipo de datos en mi entorno bash: la lista separada por dos puntos. Además de PATH, los uso para ajustar mi LOCATE_PATH, MANPATH y otros, y como un tipo de datos general en la programación de bash. Así es como configuro mi RUTA (usando las funciones):

# Add my bin directory to $PATH at the beginning, so it overrides 
addpath -f -p PATH $HOME/bin

# For Raspberry Pi development (add at end)
addpath -b -p PATH ${HOME}/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin

# remove nonexistent directories from PATH
delpath -n -p PATH

# ensure PATH contains unique entries
uniqpath -p PATH

Como el enlace de Linux Journal se conoce como "roto", puse las funciones de ruta de acceso Bash en un archivo .shar en http://pastebin.ubuntu.com/13299528/

Waltinator
fuente
3
¿Qué funciones son estas? ¿Cómo los usa el OP? Presumiblemente se describen en el enlace roto en su respuesta, que es precisamente por qué siempre queremos que las respuestas sean independientes. Edite e incluya las funciones reales en su respuesta.
terdon
@terdon: el enlace funciona para mí, puse un archivo .shar en pastebin, no publicaré líneas 1K aquí.
waltinator
Vaya, a mí también me funciona ahora. No lo hizo cuando dejé el comentario. De todos modos, la esencia de mi comentario fue que intentamos evitar vincular recursos externos en las respuestas. Queremos que la información esté aquí donde pueda actualizarse, editarse y sea inmune a la pudrición de enlaces.
terdon