Cómo mostrar las rutas en $ PATH por separado

45

No puedo entender cómo enumerar las diferentes rutas por $PATHseparado para que se vean así:

/bin
/usr/bin
/usr/local/bin

etc.

¿Alguien sabe la variable correcta para esto?

HankG
fuente
Cada ruta debe estar en una línea separada, por favor
HankG
¿Qué se debe imprimir si sus rutas contienen nuevas líneas?
Martijn
1
Y porque podemos!
Kaz Wolfe
Cross site duplicate unix.stackexchange.com/q/80151/85039
Sergiy Kolodyazhnyy

Respuestas:

57

Prueba sed:

$ sed 's/:/\n/g' <<< "$PATH"

O tr:

$ tr ':' '\n' <<< "$PATH"

O python:

$ python2 -c "import os; print os.environ['PATH'].replace(':', '\n')"

Aquí todo lo anterior sustituirá todas las ocurrencias de :con nuevas líneas \n.

heemayl
fuente
Variación en su enfoque de python:python -c 'import sys;print sys.argv[1].replace(":","\n")' $PATH
Sergiy Kolodyazhnyy
Otra variación de Python, algo hacky: python -c "print r'$PATH'.replace(':', '\n')"(usando una cadena cruda en caso de barras invertidas)
wjandrea
3
trfuncionó para mí (en mac, por cierto). Gracias.
Mike S.
Para aquellos, como yo, que quieran agregarlo a su .bash_profile, agréguelo así:alias path='tr ":" "\n" <<< "$PATH"'
Mike S.
63

Utilice la expansión de parámetros de bash :

echo "${PATH//:/$'\n'}"

Todo esto sustituye :en $PATHpor un salto de línea ( \n) e imprime el resultado. El contenido de $PATHpermanece sin cambios.
Si solo desea reemplazar el primero :, elimine la segunda barra:echo -e "${PATH/:/\n}"

Ciro
fuente
3
Creo que esta es la mejor solución porque se hace en puro bash.
ryanmjacobs
1
@ryanmjacobs ligeramente curioso: ¿qué crees que usa mi solución? : D
muru
1
por favor explique cómo funciona
Tulains Córdova
¿Cómo se llama ese tipo de operación? Es similar a sed pero no es sed. ¿Cuál es el nombre para que pueda buscar en la web más información al respecto?
Tulains Córdova
3
Siga el enlace (Expansión de parámetros) en mi respuesta y desplácese hacia abajo hasta la última sección llamada:${parameter/pattern/string}
Cyrus
27

Usando IFS:

(set -f; IFS=:; printf "%s\n" $PATH)

IFScontiene los caracteres en los que bash se divide, por lo que un IFScon :hace que bash divida la expansión de $PATHon :. printfrepite los argumentos sobre la cadena de formato hasta que se agoten los argumentos. Necesitamos deshabilitar el uso de globbing (expansión de comodines) set -fpara que los comodines en los nombres de directorio PATH no se expandan.

muru
fuente
2
¡Esto funciona correctamente con barras invertidas y espacios!
heinrich5991
14

Utilizando xargs:

xargs -n1 -d: <<< $PATH

Desde man xargs

-n max-args
          Use  at  most  max-args  arguments per command line.

 -d delim
          Input  items  are terminated by the specified character.
souravc
fuente
¿La variación en esto, como echo $PATH | xargs -n1 -d: echoser redundante o no importa?
Sergiy Kolodyazhnyy
@Serg echo $PATH | xargs -n1 -d: hará lo mismo por ti, pero usarás un shell más. El primero evaluará echo $PATHy canalizará la salida al siguiente shell para hacer el resto.
souravc
7

Aquí está el equivalente en Go:

$ cat path.go
package main

import (
    "fmt"
    "os"
    "strings"
)

func main() {
    for _, p := range strings.Split(os.Getenv("PATH"), ":") {
        fmt.Println(p)
    }
}

$ go run path.go
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
/home/nathan/.local/bin
/home/nathan/go/bin
Nathan Osman
fuente
7

Aquí hay algunos enfoques más. Estoy usando un PATHdirectorio con barras diagonales, espacios e incluso una nueva línea para mostrar que deberían funcionar con cualquier cosa (excepto la cutque falla en las nuevas líneas):

$ echo "$PATH"
/bin:usr/bin/:/usr/local/bin:/some\ horrible thing:/even 
new lines
  • Algunas formas de Perl:

    $ perl -pe 's/:/\n/g' <<<"$PATH"
    /bin
    usr/bin/
    /usr/local/bin
    /some\ horrible thing
    /even 
    new lines

    El -pmedio "imprime cada línea de entrada después de aplicar la secuencia de comandos dada por -e". El script está utilizando el operador de sustitución ( s/oldnew/) para reemplazar todo :por líneas nuevas.

    $ perl -lne 'print for split /:/' <<<"$PATH"
    /bin
    usr/bin/
    /usr/local/bin
    /some\ horrible thing
    /even 
    new lines

    El -lagrega una nueva línea a cada printllamada. Aquí, el script está dividiendo su entrada :y luego recorre cada elemento dividido y lo imprime.

    $ perl -F: -ane '$"="\n";print "@F"' <<<"$PATH"
    /bin
    usr/bin/
    /usr/local/bin
    /some\ horrible thing
    /even 
    new lines

    Las -amarcas se perlcomportan así awk: dividirá cada una de sus líneas de entrada en el carácter dado por -F( por lo tanto :, aquí) y guardará el resultado en la matriz @F. El $"es una variable especial de Perl, el "separador de lista", cuyo valor se imprime entre cada elemento de una lista impresa. Por lo tanto, establecerlo en una nueva línea hará que se print @listimprima cada elemento @listy luego una nueva línea. Aquí, lo estamos usando para imprimir @F.

    $ perl -F: -ane 'print join "\n", @F' <<<"$PATH"
    /bin
    usr/bin/
    /usr/local/bin
    /some\ horrible thing
    /even 
    new lines

    La misma idea que la anterior, solo que menos golf. En lugar de usar $", estamos explícitamente joinagregando nuevas líneas y luego imprimiendo.

  • Simple grepcon PCRE magic:

    $ grep -oP '(^|:)\K[^:]+' <<<"$PATH"
    /bin
    usr/bin/
    /usr/local/bin
    /some\ horrible thing
    /even 
    new lines

    Las -omarcas grepimprimir solamente la parte correspondiente de cada línea, por lo que cada partido se imprime en una línea separada. El -Ppermite Perl Compatible Regular Expressions (PCRE). La expresión regular busca extensiones de no- :( [^:]+) que siguen al principio de la línea ( ^) o un :carácter. El \Kes un truco PCRE que significa "descartar cualquier cosa que coincida antes de este punto" y se usa aquí para evitar imprimir :también.

  • Y una cutsolución (esta falla en las nuevas líneas, pero puede tratar con barras invertidas y espacios):

    $ cut -d: -f 1- --output-delimiter=$'\n' <<<"$PATH"
    /bin
    usr/bin/
    /usr/local/bin
    /some\ horrible thing
    /even 
    new lines

    Las opciones utilizadas son las -d:que configuran el delimitador de entrada :, lo -f 1-que significa imprimir todos los campos (desde el primero hasta el final) y lo --output-delimiter=$'\n'que configura el delimitador de salida. El $'\n'es ANSI C citando y es una manera de imprimir un carácter de nueva línea en la cáscara.

En todos los ejemplos anteriores, estoy usando el operador string ( <<<) here de bash (y algunos otros shells ) para pasar una cadena como entrada a un programa. Entonces command <<<"foo"es equivalente a echo -n "foo" | command. Tenga en cuenta que siempre estoy citando "$PATH", sin las comillas, el shell habría comido el carácter de nueva línea.


@ 7stud dio otro enfoque en los comentarios que es demasiado bueno para no incluir:

$ perl -0x3a -l012 -pe '' <<<"$PATH"

Eso es lo que se conoce como golf . El -0especifica el separador de registro de entrada como un número octal o hexadecimal. Esto es lo que define una "línea" y su valor predeterminado es \nun carácter de nueva línea. Aquí, lo estamos configurando en un :que está x3aen hexadecimal (try printf '\x3a\n'). El -lhace tres cosas. Primero, elimina el separador de registro de entrada ( $/) del final de cada línea, eliminando efectivamente el :aquí, y segundo, establece el separador de registro de salida ( $\) a cualquier valor octal o hexadecimal que se le dé ( 012es \n). Si $\se define, se agrega al final de cada printllamada, por lo que se agregará una nueva línea a cada una print.

La -pevoluntad p rint cada línea de entrada después de aplicar la secuencia de comandos dada por -e. ¡Aquí no hay script porque todo el trabajo se realiza mediante las banderas de opciones como se describe arriba!

terdon
fuente
¡Tus perl one liners no son lo suficientemente oscuros! He aquí una versión en la que no hay ningún código para la opción -e para ejecutar porque los otros interruptores manejan todo: perl -0x3a -l012 -pe '' <<<$PATH. Explicación: -0establece el separador de registro de entrada (especificado en notación hexadecimal / octal, x3A es un signo de dos puntos), -lhace dos cosas: 1) divide el separador de registro de entrada, 2) establece el separador de registro de salida si se especifica uno (en notación octal) , 012 es una nueva línea). Un -pbucle imprime el valor de $ _, que será leído en cada línea .
7stud
También tenga en cuenta que sus ejemplos usando -Fpuede eliminar los -ay las -nbanderas, como -F convierte automáticamente en los que: perl -F: -e 'print(join "\n", @F);' <<<$PATH. Ver perlrun . Buenos ejemplos.
7studio
@ 7stud wow, eso es realmente genial, ¡gracias! Descaradamente robado y agregado a mi respuesta (supongo que no te importa, siéntete libre de publicarlo como respuesta y lo eliminaré). Y gracias por la -Finformación. He estado usando -Fcombinados -andurante años, nunca me di cuenta de que uno implicaba a los demás. Por cierto, vi que Serg mencionaba Code Golf , pero creo que también disfrutarías de Unix y Linux si te gusta este tipo de cosas.
terdon
Hey gracias. Una aclaración sobre esta declaración: Finalmente, -ltambién agrega el separador de registro de salida dado al final de cada llamada impresa. De hecho, print siempre agrega el separador de registro de salida,, $/al final de una cadena, si el separador de registro de salida está definido. Por defecto es undef. Entonces, -ljus establece $/, y eso hace que print agregue la nueva línea al final de la cadena.
7stud
Supongo que no te importa , para nada. :)
7stud
6

Probablemente, la única forma en que no se ha mencionado es la forma en que lo he estado usando durante años:

echo $PATH | tr ":" "\n"

entonces, en su .profile o .bash_profile o lo que sea, puede agregar:

alias path='echo $PATH | tr ":" "\n"'

imbécil
fuente
1
Esta respuesta ya lo mencionó ..
heemayl
Sí, pero me gusta la sugerencia para el alias.
7stud
6

En esta respuesta:

  1. do
  2. Pitón
  3. Rubí
  4. Awk alternativo

1. C

Como ya se han tomado todos los lenguajes de secuencias de comandos, iré con C. Es bastante fácil obtener variables de entorno con get_env()función (consulte la documentación de la Biblioteca GNU C ). El resto es simplemente manipulación de personajes.

bash-4.3$ cat get_path.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    char *path = getenv("PATH");
    int length = strlen(path) -1;
    for(int i=0;i<=length;i++){
        if (path[i] == ':')
            path[i] = '\n';
        printf("%c",path[i]);
    }
    printf("\n");
    return 0;
}
bash-4.3$ gcc get_path.c
bash-4.3$ ./a.out 
/home/xieerqi/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
/opt/microchip/xc16/v1.25/bin
/opt/microchip/xc32/v1.40/bin
/opt/microchip/xc8/v1.35/bin
/home/xieerqi/bin
/home/xieerqi/bin/sh

2. Python

Pero también porque "por qué no", aquí hay una versión alternativa de Python a través de argumentos de línea de comando sys.argv

python -c 'import sys; print "\n".join(sys.argv[1].split(":"))' "$PATH"

3. Ruby

Ruby no viene con Ubuntu por defecto, a diferencia del compilador de C y el intérprete de Python, pero si alguna vez lo usa, la solución en Ruby sería esta:

ruby -ne 'puts $_.split(":")' <<< "$PATH"

Como lo sugirió 7stud (¡Muchas gracias!) En los comentarios , esto también se puede acortar con

ruby -F: -ane 'puts $F' <<<$PATH

y de esta manera

ruby -0072 -ne 'puts chomp' <<<$PATH

4. awk alternativo

Podemos utilizar la split()función para desglosar la línea leída en una matriz y usar el for-eachbucle para imprimir cada elemento en una línea separada.

awk '{split($0,arr,":"); for(var in arr) print arr[var]}' <<< $PATH
Sergiy Kolodyazhnyy
fuente
1
Buen ejemplo de rubí. puts¡Imprime automáticamente los elementos de una matriz en líneas separadas! También puede hacer que los interruptores hagan la división: ruby -F: -ane 'puts $F' <<<$PATH Explicación: se -Festablece $;en el carácter especificado, que es el separador predeterminado utilizado por String :: split ($; tiene un valor predeterminado de nil, que se divide en espacios en blanco). -allama a $ _. split, donde $ _ es una línea leída usando gets ()) y asigna la matriz resultante a $F.
7stud
@ 7studios oye, gracias! :) No sabía que había -Fbandera, recién estoy comenzando con Ruby, así que estoy haciendo las cosas de una manera un poco tosca.
Sergiy Kolodyazhnyy
No, esto es algo oscuro. Su único forro es muy fácil de leer, y la claridad siempre debe triunfar sobre la brevedad.
7stud
Jaja Me di cuenta de uno más corto: ruby -0072 -ne 'puts chomp' <<<$PATH. Explicación: -0establece el separador de registro de entrada (especifique un carácter en formato octal; 072 es dos puntos). Ruby emplea $ _ de manera similar a Perl. El método gets () (usado en el -nbucle) establece $ _ en la línea actual que se lee. Y chomp()sin un argumento, muerde $ _. Todavía me gusta más el tuyo. :)
7stud
@ 7stud en realidad, el que publicaste anteriormente, con -Fbandera, es más corto. Si lo hace, echo "ruby -F: -ane 'puts $F' <<<$PATH" |wc -c me dice que es 271, bytes, pero el número octal es 276. Eso es para todo el comando, por supuesto, si consideramos que solo el código en sí puts $Fes claramente más corto. :) Por cierto, ¿sabes sobre Code Golf ? Es el sitio de soluciones para programar rompecabezas en el menor número de bytes. Hay una pregunta relacionada con esta: codegolf.stackexchange.com/q/96334/55572
Sergiy Kolodyazhnyy
4

¡Necesitamos más Java!

public class GetPathByLine {
    public static void main(String[] args) {
        for (String p : System.getenv("PATH").split(":")) {
            System.out.println(p);
        }
    }
}

Guarde esto GetPathByLine.javay compílelo usando:

javac GetPathByLine.java

Corre con:

java GetPathByLine

┌─[17:06:55]─[kazwolfe@BlackHawk]
└──> ~ $ cat GetPathByLine.java 
public class GetPathByLine {
    public static void main(String[] args) {
        for (String p : System.getenv("PATH").split(":")) {
            System.out.println(p);
        }
    }
}
┌─[17:06:58]─[kazwolfe@BlackHawk]
└──> ~ $ javac GetPathByLine.java 
┌─[17:07:02]─[kazwolfe@BlackHawk]
└──> ~ $ java GetPathByLine 
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
Kaz Wolfe
fuente
Wow, esto es exagerado. Esto se puede hacer en una sola línea en Python 3:python3 -c "import os; [print(p) for p in os.getenv('PATH').split(':')]"
wjandrea
1
@wjandrea ¡Ese es el punto! : D
Kaz Wolfe el
3

A través de awk.

echo $PATH | awk -F: '{for(i=1;i<=NF;i++)print $i}'

A través de pitón.

$ echo $PATH | python3 -c 'import fileinput
for line in fileinput.input():
    for i in line.split(":"):
        print(i)'

Tenga en cuenta que la sangría es muy importante en Python.

Avinash Raj
fuente
Short awk:echo $PATH | awk '{gsub(/\:/,"\n");print}'
Sergiy Kolodyazhnyy
I'mm no sé por qué te molestas con fileinputcuando uno podía utilizar input:python3 -c 'print(*input().split(":"), sep="\n")' <<< "$PATH"
wjandrea
2

Utilizo las "Funciones de ruta de acceso Bash" de Stephen Collyer (vea su artículo en Linux Journal ). Me permite usar la "lista separada por dos puntos" como un tipo de datos en la programación de shell. Por ejemplo, puedo generar una lista de todos los directorios en el directorio actual:

dirs="";for i in * ; do if [ -d $i ] ; then addpath -p dirs $i; fi; done  

Luego, listpath -p dirsproduce una lista.

Waltinator
fuente
2

Explicación para la respuesta @Cyrus

echo "${PATH//:/$'\n'}"

Notas:

Cita ANSI-C : explica $ 'some \ ntext'

Expansión de parámetros de Shell : explica $ {parámetro / patrón / cadena}. Si el patrón comienza con '/', todas las coincidencias del patrón se reemplazan con una cadena.

Entonces tenemos:

  1. un patrón /: que comienza con '/' para reemplazar todas las coincidencias
  2. una cadena $ '\ n' que se cita con la contracción $ 'anytext' para tratar el nuevo símbolo de línea (\ n).
victorq10
fuente
1

Otra forma AWK es tratar cada directorio como un registro separado , en lugar de como un campo separado .

awk 'BEGIN{RS=":"} {print $0}' <<<"$PATH"

Encuentro esa sintaxis particularmente intuitiva. Pero, si lo desea, puede acortarlo haciendo print $0implícito (es la acción predeterminada y se 1evalúa como verdadera, lo que hace que se haga para cada línea):

awk 'BEGIN{RS=":"} 1' <<<"$PATH"

El separador de registro de entrada y salida predeterminado de AWK es la nueva línea (salto de línea). Al configurar el separador de registro de entrada ( RS) :antes de leer la entrada, AWK analiza automáticamente un delimitado por dos puntos $PATHen sus nombres de directorio constitutivos. AWK se expande $0a cada registro completo, la nueva línea sigue siendo el separador de registros de salida , y no gsubse necesita hacer un bucle .

ek@Io:~$ echo "$PATH"
/home/ek/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
ek@Io:~$ awk 'BEGIN{RS=":"} {print $0}' <<<"$PATH"
/home/ek/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin

AWK a menudo se usa para analizar registros en campos separados, pero no hay necesidad de eso solo para construir una lista de nombres de directorio.

Esto funciona incluso para entradas que contienen espacios en blanco (espacios y pestañas), incluso múltiples espacios en blanco consecutivos:

ek@Io:~$ awk 'BEGIN{RS=":"} {print $0}' <<<$'ab\t\t c:de    fg:h'
ab               c
de    fg
h

Es decir, a menos que haga que AWK reconstruya el registro (consulte a continuación), no hay problema en tener espacios o pestañas (los separadores de campo predeterminados) en la entrada. Su PATHprobablemente no contiene espacios en un sistema Ubuntu, pero si lo hace, esto todavía va a funcionar.


Vale la pena mencionar, como nota al margen, que la capacidad de AWK de interpretar un registro como una colección de campos se vuelve útil para el problema relacionado de construir una tabla de componentes de directorio :

ek@Io:~$ awk -F/ 'BEGIN{RS=":"; OFS="\t"} {$1=$1; print $0}' <<<"$PATH"
        home    ek      bin
        usr     local   sbin
        usr     local   bin
        usr     sbin
        usr     bin
        sbin
        bin
        usr     games
        usr     local   games
        snap    bin

La curiosa $1=$1asignación sirve para forzar a AWK a reconstruir el registro .

(Esto es probablemente más útil para casos en los que se debe realizar un procesamiento adicional en los componentes, que para el ejemplo exacto que se muestra de simplemente imprimir la tabla).

Eliah Kagan
fuente
1
jq -Rr 'gsub(":";"\n")' <<<$PATH
David Fetter
fuente
0

Cómo mostrar las rutas en $ PATH por separado

Estas son mis formas preferidas de hacerlo en función de mis respectivos casos de uso y preocupaciones sobre la compatibilidad y el uso de recursos.

tr

Primero, si necesita una solución rápida, fácil de recordar y legible, simplemente repítala y canalícela PATHpara traducir ( tr) para convertir los dos puntos en nuevas líneas:

echo $PATH | tr : "\n"

Tiene la desventaja de usar dos procesos debido a la tubería, pero si solo estamos pirateando una terminal, ¿realmente nos importa eso?

Expansión de parámetros de shell de Bash

Si desea una solución bastante permanente en su .bashrcuso interactivo, puede usar el alias del siguiente comando path, pero la legibilidad de esta solución es cuestionable:

alias path="echo \"${PATH//:/$'\n'}\""

Si el patrón comienza con '/', todas las coincidencias del patrón se reemplazan con una cadena. Normalmente solo se reemplaza el primer partido.

El comando anterior sustituye los dos puntos con nuevas líneas utilizando la expansión de parámetros de Shell de Bash :

${parameter/pattern/string}

Para explicarlo:

          # v--v---------delimiters, a'la sed
echo "${PATH//:/$'\n'}"
          #  ^^ ^^^^^----string, $ required to get newline character.
          #   \----------pattern, / required to substitute for *every* :.

Buena suerte recordando esto cuando solo estás pirateando la línea de comando, si no lo has aliasado, sin embargo.

IFS, probado en Bash y Dash

Alternativamente, un enfoque bastante compatible, legible y comprensible que no se base en nada más que el shell es usar la siguiente función (sugiero en su .bashrc).

La siguiente función convierte temporalmente el separador de campo interno (o de entrada) (IFS) en dos puntos, y cuando se le da una matriz printf, se ejecuta hasta que la matriz se agote:

path () {
    local IFS=:
    printf "%s\n" ${PATH}
    }

Este método de creación de la función , IFSy printfse proporcionan para por posix, así que debería funcionar en la mayoría posix-como conchas (especialmente DASH, que Ubuntu generalmente alias como sh).

Pitón

¿Deberías usar Python para esto? Tú podrías. Este es el comando Python más corto que se me ocurre para esto:

python -c "print('\n'.join('${PATH}'.split(':')))"

o solo Python 3 (¿y quizás más legible?):

python3 -c "print(*'${PATH}'.split(':'), sep='\n')"

También deberían funcionar en cualquier entorno de shell habitual, siempre que tenga Python.

Aaron Hall
fuente
0

Esta solución es más simple que las soluciones Java , C , go y awk :

$ LPATH=$PATH wine cmd /c echo %LPATH::=$'\n'% 2> /dev/null
/usr/local/bin
/usr/local/sbin
/usr/bin
/usr/sbin

Aquí hay otra gran posibilidad:

$ jrunscript -classpath /usr/share/java/bsh.jar -e 'print(java.lang.System.getenv("PATH").replaceAll(":","\n"))'
/usr/local/bin
/usr/local/sbin
/usr/bin
/usr/sbin

Esto requeriría instalar algunas dependencias:

sudo apt-get install openjdk-8-jdk-headless bsh
Gungwald
fuente