Como exibir os caminhos em $ PATH separadamente

45

Não consigo descobrir como listar os vários caminhos $PATHseparadamente, para que eles fiquem assim:

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

etc.

Alguém sabe a variável correta para isso?

HankG
fonte
Cada caminho deve estar em uma linha separada por favor
HankG
O que deve ser impresso se seus caminhos contiverem novas linhas?
Martijn 23/03
1
E porque nós podemos!
Kaz Wolfe 16/10
Duplicação entre sites unix.stackexchange.com/q/80151/85039
Sergiy Kolodyazhnyy

Respostas:

57

Tente sed:

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

Ou tr:

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

Ou python:

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

Aqui, todos os itens acima substituirão todas as ocorrências de :por novas linhas \n.

heemail
fonte
Variação na sua abordagem python:python -c 'import sys;print sys.argv[1].replace(":","\n")' $PATH
Sergiy Kolodyazhnyy 15/10
Outra variação do Python, meio hacky: python -c "print r'$PATH'.replace(':', '\n')"(usando uma corda em bruto no caso de barras invertidas)
wjandrea
3
trtrabalhou para mim (no mac, aliás). Obrigado.
Mike S.
Para aqueles como eu, que desejam adicioná-lo aos seus .bash_profile, adicione-os assim: #alias path='tr ":" "\n" <<< "$PATH"'
Mike S.
63

Use a expansão de parâmetros do bash :

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

Tudo isso substitui :em $PATHpor uma nova linha ( \n) e imprime o resultado. O conteúdo de $PATHpermanece inalterado.
Se você deseja substituir apenas o primeiro :, remova a segunda barra:echo -e "${PATH/:/\n}"

Cyrus
fonte
3
Penso que esta é a melhor solução porque é feita em pura festa.
Ryanmjacobs 22/03
1
@ryanmjacobs levemente curioso: o que você acha que minha solução usa? : D
muru 23/03
1
por favor, explique como isso funciona.
Tulains Córdova 23/03
Como é chamado esse tipo de operação? É semelhante ao sed, mas não é sed. Qual é o nome dele para que eu possa pesquisar na web para obter mais informações sobre ele.
Tulains Córdova 23/03
3
Siga o link (expansão de parâmetro) na minha resposta e role para baixo até a última segunda seção chamada:${parameter/pattern/string}
Cyrus
27

Usando IFS:

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

IFSmantém os caracteres nos quais o bash se divide, portanto, um IFScom :faz com que o bash divida a expansão de $PATHon :. printffaz um loop dos argumentos sobre a string de formato até que os argumentos se esgotem. Precisamos desativar o globbing (expansão de curinga) usando set -fpara que os curingas nos nomes de diretório PATH não sejam expandidos.

muru
fonte
2
Isso funciona corretamente com barras invertidas e espaços!
precisa
14

Usando xargs:

xargs -n1 -d: <<< $PATH

De 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
fonte
haveria variação nisso, como echo $PATH | xargs -n1 -d: echoredundante ou não importa?
Sergiy Kolodyazhnyy
O @Serg echo $PATH | xargs -n1 -d: fará o mesmo por você, mas você usará mais um shell. O primeiro avaliará echo $PATHe canalizará a saída para o próximo shell para fazer o resto.
Souravc
7

Aqui está o equivalente em 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
fonte
7

Aqui estão mais algumas abordagens. Estou usando um PATHcom diretórios contendo barras invertidas, espaços e até uma nova linha para mostrar que eles devem funcionar com qualquer coisa (exceto a cutque falha em novas linhas):

$ echo "$PATH"
/bin:usr/bin/:/usr/local/bin:/some\ horrible thing:/even 
new lines
  • Algumas maneiras Perl:

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

    O -pmeio "imprime todas as linhas de entrada após aplicar o script fornecido por -e". O script está usando o operador de substituição ( s/oldnew/) para substituir todos :por novas linhas.

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

    O -ladiciona uma nova linha a cada printchamada. Aqui, o script está dividindo sua entrada :e, em seguida, faz um loop sobre cada elemento de divisão e a imprime.

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

    As -amarcas perlse comportam assim awk: dividirá cada uma de suas linhas de entrada no caractere fornecido por -F(então :, aqui) e salvará o resultado na matriz @F. A $"é uma variável especial Perl, o "separador de lista", cujo valor é impressa entre cada elemento de uma lista impressa. Portanto, defini-lo como uma nova linha fará com que print @listcada elemento seja impresso @liste, em seguida, uma nova linha. Aqui, estamos usando-o para imprimir @F.

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

    A mesma idéia acima, apenas menos golfe. Em vez de usar $", estamos explicitamente joininserindo a matriz com novas linhas e depois imprimindo.

  • Simples grepcom a magia PCRE:

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

    O -ofaz grepimprimir apenas a parte correspondente de cada linha, para que cada correspondência seja impressa em uma linha separada. O -Ppermite Perl Compatible Regular Expressions (PCRE). O regex está procurando trechos de não :( [^:]+) que seguem o início da linha ( ^) ou um :caractere. O \Ké um truque PCRE que significa "nada descarte combinado antes deste ponto" e é usado aqui para evitar a impressão do :bem.

  • E uma cutsolução (essa falha nas novas linhas, mas pode lidar com barras invertidas e espaços):

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

    As opções utilizadas são: -d:define o delimitador de entrada :, o -f 1-que significa imprimir todos os campos (do 1º ao final) e --output-delimiter=$'\n'define o delimitador de saída. A citação$'\n' é ANSI C e é uma maneira de imprimir um caractere de nova linha no shell.

Em todos os exemplos acima, estou usando o operador string ( <<<) de bash (e alguns outros shells) aqui para passar uma string como entrada para um programa. Então command <<<"foo"é equivalente a echo -n "foo" | command. Observe que estou sempre citando "$PATH", sem as aspas, o shell teria comido o caractere de nova linha.


O @ 7stud deu outra abordagem nos comentários, que é boa demais para não incluir:

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

Isso é conhecido como golfe . O -0especifica o separador de registro de entrada como um número octal ou hexadecimal. É isso que define uma "linha" e seu valor padrão é \num caractere de nova linha. Aqui, estamos definindo-o como um :que está x3aem hexadecimal (tentativa printf '\x3a\n'). O -lfaz três coisas. Primeiro, ele remove o separador de registro de entrada ( $/) do final de cada linha - removendo efetivamente o :here - e, em segundo lugar, define o separador de registro de saída ( $\) para qualquer valor octal ou hexadecimal que for dado ( 012é \n). Se $\definido, ele será adicionado ao final de cada printchamada, resultando em uma nova linha anexada a cada uma print.

A -pevontade p rint cada linha de entrada após a aplicação do roteiro dada por -e. Aqui não há script, porque todo o trabalho é realizado pelos sinalizadores de opção, conforme descrito acima!

terdon
fonte
Seus liners perl one não são suficientemente obscuros! Aqui está uma versão em que não há nenhum código para a opção -e para executar porque os outros switches lidar com tudo: perl -0x3a -l012 -pe '' <<<$PATH. Explicação: -0define o separador de registros de entrada (especificado em notação hex / octal, x3A é dois pontos), -lfaz duas coisas: 1) chomps o separador de registros de entrada, 2) define o separador de registros de saída se um for especificado (em notação octal , 012 é uma nova linha). Um -ploop imprime o valor de $ _, que será lido em cada linha .
7stud
Observe também que os exemplos usando -Fpode eliminar os -ae -nbandeiras, como -F transforma aqueles em automaticamente perl -F: -e 'print(join "\n", @F);' <<<$PATH. Veja perlrun . Bons exemplos.
7stud
@ 7stud wow, isso é realmente brilhante, obrigado! Roubado descaradamente e adicionado à minha resposta (presumo que você não se importe, fique à vontade para postá-la como resposta e eu a excluirei). E obrigado pela -Finformação. Eu uso -Fcombinado -anhá anos, nunca percebi que um implicava os outros. A propósito, vi Serg mencionando o Code Golf , mas acho que você também gostaria de Unix e Linux se você gosta desse tipo de coisa.
terdon
Ei, obrigado. Um esclarecimento sobre esta afirmação: Finalmente, -ltambém adiciona o separador de registros de saída fornecido ao final de cada chamada de impressão. De fato, a impressão sempre adiciona o separador de registros de saída,, $/ao final de uma string - se o separador de registros de saída estiver definido. Por padrão, é undef. Então, -ljus define $/e isso faz com que o print adicione a nova linha no final da string.
7stud
Presumo que você não se importe - de maneira alguma. :)
7stud
6

Nesta resposta:

  1. C
  2. Pitão
  3. Rubi
  4. Awk alternativo

1. C

Como todas as linguagens de script já foram utilizadas, irei com o C. É muito fácil obter variáveis ​​de ambiente com a get_env()função (consulte a documentação da GNU C Library ). O resto é simplesmente manipulação de caracteres

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

Mas também porque "por que não", aqui está a versão alternativa do python via argumentos da linha de comando sys.argv

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

3. Ruby

O Ruby não vem com o Ubuntu por padrão, ao contrário do compilador C e do interpretador Python, mas se você o usar, a solução no Ruby seria a seguinte:

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

Como sugerido pelo 7stud (Muito obrigado!) Nos comentários , isso também pode ser abreviado com

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

e assim

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

4. Awk alternativo

Podemos utilizar a split()função para dividir a linha lida em matriz e usar o for-eachloop para imprimir cada item em uma linha separada.

awk '{split($0,arr,":"); for(var in arr) print arr[var]}' <<< $PATH
Sergiy Kolodyazhnyy
fonte
1
Belo exemplo de rubi. putsimprime automaticamente os elementos de uma matriz em linhas separadas! Você também pode fazer com que os comutadores façam a divisão: ruby -F: -ane 'puts $F' <<<$PATH Explicação: -Fdefine $;o caractere especificado, que é o separador padrão usado por String :: split ($; tem um valor padrão nil, que se divide em espaço em branco). -achama $ _. split, onde $ _ é uma linha lida usando gets ()) e atribui a matriz resultante a $F.
7stud
@ 7stud hey, obrigado! :) Não sabia da -Fbandeira - estou apenas começando com Ruby, fazendo as coisas de maneira um pouco grosseira.
Sergiy Kolodyazhnyy
Não, isso é coisa obscura. Seu liner é muito legível e a clareza deve prevalecer sobre a brevidade todas as vezes.
7stud
Hah. Eu descobri um mais curto: ruby -0072 -ne 'puts chomp' <<<$PATH. Explicação: -0define o separador de registro de entrada (especifique um caractere no formato octal; 072 é dois pontos). Ruby emprega $ _ de maneira semelhante ao Perl. O método gets () (usado no -nloop) define $ _ para a linha atual que é lida. E chomp()sem um argumento, chomps $ _. Eu ainda gosto mais do seu. :)
7stud
@ 7stud, na verdade, o que você postou anteriormente, com -Fsinalizador, é mais curto. Se você fizer echo "ruby -F: -ane 'puts $F' <<<$PATH" |wc -c isso, me informará que são 271 bytes, mas o número octal é 276. Isso é para todo o comando, é claro, se considerarmos que apenas o código em si puts $Fé claramente mais curto. :) A propósito, você conhece o Code Golf ? É o site de soluções para a programação de quebra-cabeças no menor número de bytes. Há uma questão relacionada com esta: codegolf.stackexchange.com/q/96334/55572
Sergiy Kolodyazhnyy
5

Provavelmente, a única maneira que não foi mencionada é a maneira que eu a uso há anos:

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

portanto, em seu .profile ou .bash_profile ou o que for, você pode adicionar:

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

idiota
fonte
1
Esta resposta mencionado já ..
heemayl
Sim, mas gosto da sugestão para o pseudônimo.
7stud
4

Precisamos de mais Java!

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

Salve isso GetPathByLine.javae compile usando:

javac GetPathByLine.java

Correr com:

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
fonte
Uau, isso é um exagero. Você pode fazer isso em uma linha em Python 3:python3 -c "import os; [print(p) for p in os.getenv('PATH').split(':')]"
wjandrea
1
@wjandrea Esse é o ponto! : D
Kaz Wolfe
3

Através do awk.

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

Através de python.

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

Observe que o recuo é muito importante em python.

Avinash Raj
fonte
Mais curto awk:echo $PATH | awk '{gsub(/\:/,"\n");print}'
Sergiy Kolodyazhnyy 14/10
I'mm não sei por que você se preocupar com fileinputquando você pode simplesmente usar input:python3 -c 'print(*input().split(":"), sep="\n")' <<< "$PATH"
wjandrea
2

Eu uso "Bash Path Functions" de Stephen Collyer (veja seu artigo no Linux Journal ). Permite-me usar a "lista separada por dois pontos" como um tipo de dados na programação shell. Por exemplo, eu posso produzir uma lista de todos os diretórios no diretório atual:

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

Então, listpath -p dirsproduz uma lista.

waltinator
fonte
2

Explicação da resposta @Cyrus

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

Notas:

Citação ANSI-C - explica $ 'some \ ntext'

Expansão de parâmetros do shell - explica $ {parameter / pattern / string}. Se o padrão começa com '/', todas as correspondências do padrão são substituídas por string.

Então nós temos:

  1. um padrão /: que começa com '/' para substituir todas as correspondências
  2. uma string $ '\ n' que é citada com a contração $ 'anytext' para tratar o novo símbolo de linha (\ n).
victorq10
fonte
1

Outra maneira do AWK é tratar cada diretório como um registro separado , e não como um campo separado .

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

Acho essa sintaxe particularmente intuitiva. Mas, se quiser, você pode reduzi-lo, tornando print $0implícito (é a ação padrão e 1avalia como verdadeira, fazendo com que seja feito para cada linha):

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

O separador de registro de entrada e saída padrão do AWK é a nova linha (quebra de linha). Ao definir o separador de registros de entrada ( RS) como :antes de ler a entrada, o AWK analisa automaticamente dois pontos delimitados por dois pontos $PATHem seus nomes de diretório constitutivos. O AWK se expande $0para cada registro inteiro, a nova linha continua sendo o separador de registros de saída e não gsubé necessário fazer loop ou é necessário.

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

O AWK é frequentemente usado para analisar registros em campos separados, mas não há necessidade disso apenas para construir uma lista de nomes de diretórios.

Isso funciona mesmo para entradas que contêm espaços em branco (espaços e tabulações), até vários espaços em branco consecutivos:

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

Ou seja, a menos que você faça com que o AWK reconstrua o registro (veja abaixo), não há problema em ter espaços ou tabulações (os separadores de campo padrão) na entrada. Seu PATHprovavelmente não contém espaços em um sistema Ubuntu, mas se isso acontecer, isso ainda vai funcionar.


Vale ressaltar, como observação lateral, que a capacidade do AWK de interpretar um registro como uma coleção de campos se torna útil para o problema relacionado de construir uma tabela de componentes de diretório :

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

A $1=$1designação curiosa serve para forçar o AWK a reconstruir o registro .

(Isso é provavelmente mais útil nos casos em que um processamento adicional deve ser feito nos componentes, do que no exemplo exato mostrado simplesmente da impressão da tabela.)

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

Como exibir os caminhos em $ PATH separadamente

Essas são as minhas maneiras preferidas de fazer isso com base nos meus respectivos casos de uso e preocupações sobre compatibilidade e uso de recursos.

tr

Primeiro, se você precisar de uma solução rápida, fácil de lembrar e legível, basta fazer eco PATHe canalizá-la para translate ( tr) para transformar os dois pontos em novas linhas:

echo $PATH | tr : "\n"

Ele tem a desvantagem de usar dois processos por causa do canal, mas se estamos apenas invadindo um terminal, realmente nos importamos com isso?

Expansão do parâmetro shell do Bash

Se você deseja uma solução bastante permanente .bashrcpara uso interativo, pode usar o seguinte comando path, mas a legibilidade dessa solução é questionável:

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

Se o padrão começar com '/', todas as correspondências do padrão serão substituídas por uma sequência. Normalmente, apenas a primeira partida é substituída.

O comando acima substitui os dois pontos por novas linhas usando a expansão de parâmetros de shell do Bash :

${parameter/pattern/string}

Para explicar isso:

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

Boa sorte lembrando-se disso quando você está apenas invadindo a linha de comando, se ainda não tiver o alias.

IFS, testado no Bash and Dash

Como alternativa, uma abordagem razoavelmente compatível, legível e compreensível que não dependa de nada além do shell é usar a seguinte função (sugiro no seu .bashrc.)

A função a seguir transforma temporariamente o Separador de Campo Interno (ou Entrada) (IFS) em dois pontos e, quando uma matriz é fornecida printf, ela é executada até que a matriz seja usada:

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

Este método de criação da função , IFSe printfsão fornecidos pelo POSIX, assim que deve funcionar na maioria POSIX-como conchas (especialmente Dash, que Ubuntu geralmente aliases como sh).

Pitão

Você deve usar o Python para isso? Você poderia. Este é o comando Python mais curto que consigo pensar nisso:

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

ou apenas Python 3 (e talvez mais legível?):

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

Eles devem funcionar em qualquer ambiente shell comum, desde que você possua Python.

Aaron Hall
fonte
0

Esta solução é mais simples que as soluções Java , C , go e awk :

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

Aqui está outra grande possibilidade:

$ 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

Isso exigiria a instalação de algumas dependências:

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