¿Alguien ha escrito una función bash para agregar un directorio a $ PATH solo si aún no está allí?
Normalmente agrego a PATH usando algo como:
export PATH=/usr/local/mysql/bin:$PATH
Si construyo mi RUTA en .bash_profile, entonces no se lee a menos que la sesión en la que estoy sea una sesión de inicio de sesión, lo que no siempre es cierto. Si construyo mi RUTA en .bashrc, entonces se ejecuta con cada subshell. Entonces, si inicio una ventana de Terminal y luego ejecuto la pantalla y luego ejecuto un script de shell, obtengo:
$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....
Intentaré construir una función bash llamada add_to_path()
que solo agrega el directorio si no está allí. Pero, si alguien ya ha escrito (o encontrado) algo así, no pasaré el tiempo en ello.
Respuestas:
De mi .bashrc:
Tenga en cuenta que PATH ya debería estar marcado como exportado, por lo que no es necesario volver a exportar. Esto verifica si el directorio existe y es un directorio antes de agregarlo, lo cual puede no importarle.
Además, esto agrega el nuevo directorio al final de la ruta; para poner al principio, use en
PATH="$1${PATH:+":$PATH"}"
lugar de laPATH=
línea anterior .fuente
":$PATH:"
lugar de solo"$PATH"
${PATH:+"$PATH:"}
$ 1"${variable:+value}
significa verificar sivariable
está definido y tiene un valor no vacío, y si lo hace da el resultado de la evaluaciónvalue
. Básicamente, si PATH no está en blanco para empezar, lo establece en"$PATH:$1"
; si está en blanco, lo establece en solo"$1"
(tenga en cuenta la falta de dos puntos).Ampliando la respuesta de Gordon Davisson, esto admite múltiples argumentos
Entonces puedes hacer pathappend ruta1 ruta2 ruta3 ...
Por anteponer,
Similar a pathappend, puedes hacer
pathprepend ruta1 ruta2 ruta3 ...
fuente
pathprepend P1 P2 P3
y terminar conPATH=P1:P2:P3
. Para obtener este comportamiento, cambiefor ARG in "$@" do
afor ((i=$#; i>0; i--)); do ARG=${!i}
Aquí hay algo de mi respuesta a esta pregunta combinada con la estructura de la función de Doug Harris. Utiliza expresiones regulares de Bash:
fuente
$1
lugar de${1}
Ponga esto en los comentarios a la respuesta seleccionada, pero los comentarios no parecen admitir el formato PRE, por lo tanto, agregue la respuesta aquí:
@ Gordon-Davisson No soy un gran fanático de las citas y concatenaciones innecesarias. Suponiendo que está utilizando una versión bash> = 3, en su lugar, puede usar expresiones regulares integradas de bash y hacer:
Esto maneja correctamente los casos en los que hay espacios en el directorio o la RUTA. Hay algunas dudas sobre si el motor de expresiones regulares integrado de bash es lo suficientemente lento como para que esto sea menos eficiente que la concatenación e interpolación de cadenas que su versión, pero de alguna manera me parece más estéticamente limpio.
fuente
formatting using the backtick
solo son compatibles, pero no obtienes ningún control de párrafo decente.unnecessary quoting
implica que sabe de antemano que$PATH
no es nulo."$PATH"
hace que sea correcto si PATH es nulo o no. Del mismo modo, si$1
contiene caracteres que pueden confundir el analizador de comandos. Poner la expresión regular entre comillas"(^|:)$1(:|$)"
evita eso.[[
y]]
. Yo prefiero citar todo lo que pueda necesitar para ser citado, a menos que lo causa a fallar, pero creo que tiene razón, y que las comillas no son realmente necesarios alrededor$PATH
. Por otro lado, me parece que tienes razón$1
.Cuando necesite que $ HOME / bin aparezca exactamente una vez al comienzo de su $ PATH y en ningún otro lugar, no acepte sustitutos.
fuente
PATH=${PATH/"
... en lugar dePATH=${PATH//"
... para que funcionara.$1
es la única entrada (sin dos puntos). La entrada se duplica.Aquí hay una solución alternativa que tiene la ventaja adicional de eliminar entradas redundantes:
El argumento único para esta función se antepone a la RUTA, y la primera instancia de la misma cadena se elimina de la ruta existente. En otras palabras, si el directorio ya existe en la ruta, se promueve al frente en lugar de agregarse como un duplicado.
La función funciona al anteponer dos puntos a la ruta para garantizar que todas las entradas tengan dos puntos al frente, y luego anteponer la nueva entrada a la ruta existente con esa entrada eliminada. La última parte se realiza utilizando la
${var//pattern/sub}
notación de bash ; vea el manual de bash para más detalles.fuente
/home/robert
en tuPATH
y en tipathadd /home/rob
.Aquí está el mío (creo que fue escrito hace años por Oscar, el administrador del sistema de mi antiguo laboratorio, todo el crédito para él), ha estado en mi bashrc durante siglos. Tiene el beneficio adicional de permitirle anteponer o agregar el nuevo directorio como desee:
Uso:
fuente
Para anteponer, me gusta la solución de @ Russell, pero hay un pequeño error: si intenta anteponer algo como "/ bin" a una ruta de "/ sbin: / usr / bin: / var / usr / bin: / usr / local / bin: / usr / sbin "reemplaza" / bin: "3 veces (cuando realmente no coincide en absoluto). Combinando una solución para eso con la solución anexa de @ gordon-davisson, obtengo esto:
fuente
Un simple alias como este a continuación debería hacer el truco:
Todo lo que hace es dividir la ruta en el carácter: y comparar cada componente con el argumento que pasa. Grep busca una coincidencia de línea completa e imprime el recuento.
Uso de la muestra:
Reemplace el comando echo con addToPath o algún alias / función similar.
fuente
Consulte ¿Cómo evitar duplicar la variable de ruta en csh? en StackOverflow para un conjunto de respuestas a esta pregunta.
fuente
Esto es lo que preparé:
Ahora en .bashrc tengo:
Versión actualizada después del comentario sobre cómo mi original no manejará directorios con espacios (gracias a esta pregunta por indicarme que use
IFS
):fuente
PATH
contiene espacios en blanco,*
,?
, o[
...]
.Documents and Settings
yProgram Files
), pero Unix ha admitido nombres de ruta que contienen espacios en blanco desde antes de que existiera Windoze.Estoy un poco sorprendido de que nadie haya mencionado esto todavía, pero puede usarlo
readlink -f
para convertir rutas relativas en rutas absolutas y agregarlas a la RUTA como tal.Por ejemplo, para mejorar la respuesta de Guillaume Perrault-Archambault,
se convierte
1. Lo básico: ¿de qué sirve esto?
El
readlink -f
comando convertirá (entre otras cosas) una ruta relativa en una ruta absoluta. Esto te permite hacer algo como2. ¿Por qué hacemos pruebas para estar en PATH dos veces?
Bueno, considere el ejemplo anterior. Si el usuario dice desde el directorio por segunda vez, será . Por supuesto, no estará presente en . Pero luego se establecerá en (el equivalente de ruta absoluta de ), que ya está en . Por lo tanto, debemos evitar agregar a una segunda vez.
pathappend .
/path/to/my/bin/dir
ARG
.
.
PATH
ARGA
/path/to/my/bin/dir
.
PATH
/path/to/my/bin/dir
PATH
Quizás lo más importante, el propósito principal de
readlink
es, como su nombre lo indica, mirar un enlace simbólico y leer el nombre de ruta que contiene (es decir, apunta a). Por ejemplo:Ahora, si dices
pathappend /usr/lib/perl/5.14
, y ya tienes/usr/lib/perl/5.14
en tu RUTA, bueno, está bien; podemos dejarlo como está. Pero, si/usr/lib/perl/5.14
no está ya en su PATH, llamamosreadlink
y nosARGA
=/usr/lib/perl/5.14.2
, y luego añadimos que aPATH
. Pero espere un minuto: si ya lo dijopathappend /usr/lib/perl/5.14
, ya lo tiene/usr/lib/perl/5.14.2
en su RUTA y, nuevamente, debemos verificarlo para evitar agregarlo porPATH
segunda vez.3. ¿Cuál es el trato con
if ARGA=$(readlink -f "$ARG")
?En caso de que no esté claro, esta línea prueba si
readlink
tiene éxito. Esto es simplemente una buena práctica de programación defensiva. Si vamos a utilizar la salida del comando m como parte del comando n (donde m < n ), es prudente verificar si el comando m falló y manejarlo de alguna manera. No creo que sea probable quereadlink
falle, pero, como se discutió en Cómo recuperar la ruta absoluta de un archivo arbitrario desde OS X y en otros lugares,readlink
es una invención de GNU. No se especifica en POSIX, por lo que su disponibilidad en Mac OS, Solaris y otros Unix no Linux es cuestionable. (De hecho, acabo de leer un comentario que dice "readlink -f
no parece funcionar en Mac OS X 10.11.6, perorealpath
funciona fuera de la caja ". Entonces, si está en un sistema que no tienereadlink
, o dondereadlink -f
no funciona, puede modificar esto script para usarrealpath
.) Al instalar una red de seguridad, hacemos que nuestro código sea algo más portátil.Por supuesto, si está en un sistema que no tiene
readlink
(orealpath
), no querrá hacerlo .pathappend .
La segunda
-d
prueba ([ -d "$ARGA" ]
) probablemente sea realmente innecesaria. No puedo pensar en ningún escenario donde$ARG
sea un directorio yreadlink
tenga éxito, pero$ARGA
no es un directorio. Simplemente copié y pegué la primeraif
declaración para crear la tercera, y dejé la-d
prueba allí por flojera.4. ¿Algún otro comentario?
Sí. Como muchas de las otras respuestas aquí, esta prueba si cada argumento es un directorio, lo procesa si lo es, y lo ignora si no lo es. Esto puede (o no) ser adecuado si está utilizando
pathappend
solo en ".
" archivos (como.bash_profile
y.bashrc
) y otros scripts. Pero, como mostró esta respuesta (arriba), es perfectamente factible usarlo de forma interactiva. Estarás muy perplejo si lo hacesComo dije anteriormente, es una buena práctica manejar los errores. Aquí hay un ejemplo:
fuente
readlink -f "$ARG"
. (2) No sé por qué sucedería (especialmente después de que la-d "$ARG"
prueba tuvo éxito), pero es posible que desee probar sireadlink
falló. (3) Parece que está pasando por altoreadlink
la función principal: asignar un nombre de enlace simbólico a un "nombre de archivo real". Si (por ejemplo)/bin
es un enlace simbólico a/bin64
, entonces las llamadas repetidas apathappend /bin
podrían dar lugar a la palabra PATH…:/bin64:/bin64:/bin64:/bin64:…
. Probablemente debería (también) verificar si el nuevo valor de$ARG
ya está enPATH
..
en mi ejemplo creo que puede considerarse como un nombre de archivo canónico. ¿Correcto?readlink
el nombre claramente implica su propósito: mira un enlace simbólico y lee el nombre de la ruta que contiene (es decir, señala). (2) Bueno, dije que no sabía porreadlink
qué fallaría. Estaba señalando que si un script o función contiene múltiples comandos, y se garantiza que el comando n fallará catastróficamente (o no tiene ningún sentido) si el comando m falló (donde m < n ), es una buena práctica prudente compruebe si el comando m falló y maneje eso de alguna manera, como mínimo, ... (Continúa)readlink
podría fallar si (a) el directorio fue renombrado o eliminado (por otro proceso) entre sus llamadas atest
yreadlink
, o (b) si/usr/bin/readlink
fue eliminado (o dañado). (3) Parece que te estás perdiendo mi punto. No te estoy animando a duplicar otras respuestas; Estoy diciendo que, al verificar si el originalARG
(desde la línea de comando) ya está enPATH
, pero no repetir la verificación para el nuevoARG
(la salida dereadlink
), su respuesta es incompleta y, por lo tanto, incorrecta. … (Continúa)fuente
De esta manera funciona bien:
fuente
Mis versiones tienen menos cuidado con las rutas vacías e insisten en que las rutas sean válidas y directorios que algunas publicadas aquí, pero encuentro una gran colección de prepend / append / clean / unique-ify / etc. las funciones de shell son útiles para la manipulación de rutas. Todo el lote, en su estado actual, está aquí: http://pastebin.com/xS9sgQsX (¡comentarios y mejoras muy bienvenidos!)
fuente
Puedes usar un perl one liner:
Aquí está en bash:
fuente
Modifiqué ligeramente la respuesta de Gordon Davisson para usar el directorio actual si no se proporciona ninguno. Entonces, puede hacerlo
padd
desde el directorio que desea agregar a su RUTA.fuente
Puede verificar si se ha configurado una variable personalizada; de lo contrario, configúrela y luego agregue las nuevas entradas:
Por supuesto, estas entradas aún podrían duplicarse si las agrega otro script, como
/etc/profile
.fuente
Este script le permite agregar al final de
$PATH
:O agregue al principio de
$PATH
:fuente
Aquí hay una manera compatible con POSIX.
Se cribbed de Guillaume Perrault-Archambault 's respuesta a esta pregunta y mike511 ' s respuesta aquí .
ACTUALIZACIÓN 2017-11-23: Error corregido por @Scott
fuente
/etc/profile
. Es posible que el directorio que está intentando agregar a PATH ya esté allí más de una vez , y su código se ahoga con eso. … (Continúa)/a/ck
a/b:/a/ck:/tr:/a/ck
, se obtiene/a/ck:/b:/a/ck:/tr:/tr:/a/ck
.