Come visualizzare i percorsi in $ PATH separatamente


45

Non riesco a capire come elencare i vari percorsi $PATHseparatamente in modo che appaiano così:

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

eccetera.

Qualcuno conosce la variabile corretta per questo?


Ogni percorso dovrebbe essere su una linea separata per favore
HankG

Cosa dovrebbe essere stampato se i percorsi contengono nuove righe?
Martijn,


Risposte:


57

Prova sed:

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

Oppure tr:

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

Oppure python:

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

Qui tutto quanto sopra sostituirà tutte le occorrenze di :con nuove righe \n.


Variazione dell'approccio al pitone:python -c 'import sys;print sys.argv[1].replace(":","\n")' $PATH
Sergiy Kolodyazhnyy,

Un'altra variante di Python, kinda hacky: python -c "print r'$PATH'.replace(':', '\n')"(usando una stringa grezza in caso di barre rovesciate)
wjandrea

3
trha funzionato per me (su Mac, BTW). Grazie.
Mike S.

Per quelli, come me, che vogliono aggiungerlo ai loro .bash_profile, aggiungilo così:alias path='tr ":" "\n" <<< "$PATH"'
Mike S.

63

Usa l' espansione dei parametri di bash :

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

Questo sostituisce all :in $PATHcon una newline ( \n) e stampa il risultato. Il contenuto di $PATHrimane invariato.
Se desideri sostituire solo il primo :, rimuovi la seconda barra:echo -e "${PATH/:/\n}"


3
Penso che questa sia la soluzione migliore perché è fatta in puro bash.
ryanmjacobs,

1
@ryanmjacobs leggermente curioso: cosa pensi che usi la mia soluzione? : D
muru,

1
per favore spiega come funziona.
Tulains Córdova,

Come si chiama quel tipo di operazione? È simile a sed ma non è sed. Come si chiama, così posso cercare sul web ulteriori informazioni a riguardo.
Tulains Córdova,

3
Segui il link (Parameter Expansion) nella mia risposta e scorri verso il basso fino all'ultima seconda sezione chiamata:${parameter/pattern/string}
Cyrus

27

Utilizzando IFS:

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

IFScontiene i personaggi su cui bash si divide, quindi un IFScon :fa bash dividere l'espansione di $PATHon :. printfesegue il loop degli argomenti sulla stringa di formato fino a quando gli argomenti sono esauriti. Dobbiamo disabilitare il globbing (espansione con caratteri jolly) in set -fmodo che i caratteri jolly nei nomi delle directory PATH non vengano espansi.


2
Funziona correttamente con barre rovesciate e spazi!
Heinrich5991,

14

Utilizzando xargs:

xargs -n1 -d: <<< $PATH

A partire dal man xargs

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

 -d delim
          Input  items  are terminated by the specified character.

la variazione su questo, come ad esempio echo $PATH | xargs -n1 -d: echoessere ridondante o non importa?
Sergiy Kolodyazhnyy,

@Serg echo $PATH | xargs -n1 -d: farà la stessa cosa per te ma userai un'altra shell. Il primo valuterà echo $PATHe invierà l' output alla shell successiva per fare il resto.
souravc,

7

Ecco l'equivalente in 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

7

Ecco alcuni altri approcci. Sto usando un PATHcon le directory che contengono barre rovesciate, spazi e persino una nuova riga per mostrare che dovrebbero funzionare con qualsiasi cosa (tranne cutquella che fallisce nelle newline):

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

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

    Il -pmezzo "stampa ogni riga di input dopo aver applicato lo script fornito da -e". Lo script utilizza l'operatore di sostituzione ( s/oldnew/) per sostituire tutto :con le nuove righe.

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

    Il -laggiunge un ritorno a capo ad ogni printchiamata. Qui, lo script sta dividendo il suo input :e quindi scorre su ogni elemento split e lo stampa.

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

    I -amake si perlcomportano come awk: dividerà ciascuna delle sue righe di input sul carattere dato da -F(quindi :, qui) e salverà il risultato nella matrice @F. La $"è una variabile speciale Perl, la "lista separatore", il cui valore viene stampato tra ogni elemento di una lista stampata. Quindi impostandolo su una nuova riga, verrà print @liststampato ogni elemento @liste quindi una nuova riga. Qui, lo stiamo usando per stampare @F.

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

    Stessa idea come sopra, solo meno giocata a golf. Invece di usare $", stiamo esplicitamente joineseguendo l'array con nuove righe e quindi stampando.

  • Semplice grepcon la magia PCRE:

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

    I -omarchi grepstampare solo la porzione corrispondente di ciascuna linea, quindi ogni partita viene stampata su una linea separata. Il -Ppermette Perl espressioni regolari compatibili (PCRE). Il regex è alla ricerca di tratti di non :( [^:]+) che seguono l'inizio della riga ( ^) o un :carattere. Si \Ktratta di un trucco PCRE che significa "scarta qualsiasi cosa abbinata prima di questo punto" e viene usato qui per evitare di stampare :anche.

  • E una cutsoluzione (questa fallisce su newline, ma può occuparsi di barre rovesciate e spazi):

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

    Le opzioni utilizzate sono quelle -d:che impostano il delimitatore di input su :, il -f 1-che significa stampare tutti i campi (dal 1 ° alla fine) e --output-delimiter=$'\n'che imposta il delimitatore di output. Il $'\n'è ANSI C citare ed è un modo per stampare un carattere di nuova riga nel guscio.

In tutti gli esempi precedenti, sto usando l' operatore string ( <<<) qui di bash (e alcune altre shell ) per passare una stringa come input per un programma. Quindi command <<<"foo"è equivalente a echo -n "foo" | command. Nota che sto sempre citando "$PATH", senza le virgolette, la shell avrebbe mangiato il carattere di nuova riga.


@ 7stud ha dato un altro approccio nei commenti che è semplicemente troppo bello per non includere:

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

Questo è ciò che è noto come il golf . La -0specifica il separatore di record di ingresso come un numero ottale o esadecimale. Questo è ciò che definisce una "linea" e il suo valore predefinito è \nun carattere di nuova riga. Qui, lo stiamo impostando su un :che è x3ain esadecimale (provare printf '\x3a\n'). La -lfa tre cose. Innanzitutto rimuove il separatore del record di input ( $/) dalla fine di ogni riga — rimuovendo effettivamente il :qui — e, in secondo luogo, imposta il separatore del record di output ( $\) su qualunque valore ottale o esadecimale che viene dato ( 012è \n). Se $\definito, viene aggiunto alla fine di ogni printchiamata, quindi ciò comporterà una nuova riga aggiunta a ciascuna print.

La -pevolontà p Rint ciascuna linea di ingresso dopo l'applicazione dello script in -e. Qui non esiste uno script perché tutto il lavoro viene svolto dai flag delle opzioni come descritto sopra!


Le tue fodere perl one non sono abbastanza oscure! Ecco una versione in cui non c'è il codice per lo switch -e per eseguire perché gli altri interruttori gestire tutto: perl -0x3a -l012 -pe '' <<<$PATH. Spiegazione: -0imposta il separatore del record di input (specificato nella notazione esadecimale / ottale, x3A è un punto e virgola), -lfa due cose: 1) crea il separatore del record di input, 2) imposta il separatore del record di output se specificato (in notazione ottale , 012 è una nuova riga). Un -pciclo stampa il valore di $ _, che verrà letto in ogni riga .
7stud

Si noti inoltre che i tuoi esempi utilizzando -Fin grado di eliminare le -ae -nbandiere, come -F trasforma quelli automaticamente: perl -F: -e 'print(join "\n", @F);' <<<$PATH. Vedi perlrun . Bei esempi.
7stud

@ 7stud Eeeek !!!, è davvero geniale, grazie! Spudoratamente rubato e aggiunto alla mia risposta (suppongo che non ti dispiaccia, sentiti libero di postarlo come risposta e lo cancellerò). E grazie per le -Finformazioni. Ho usato -Finsieme -anper anni, non ho mai capito che l'uno implicava gli altri. A proposito, ho visto Serg menzionato Code Golf , ma penso che ti piacerà anche Unix e Linux se ti piacciono queste cose.
terdon

Ehi, grazie. Un chiarimento su questa affermazione: Infine, -laggiunge anche il separatore del record di output fornito alla fine di ogni chiamata di stampa. In effetti, print aggiunge sempre il separatore del record di output $/, alla fine di una stringa, se viene definito il separatore del record di output. Di default è indefinito. Quindi -ljus imposta $/e ciò fa sì che print aggiunga la nuova riga alla fine della stringa.
7stud

Presumo che non ti dispiaccia - Niente affatto. :)
7stud

6

Probabilmente l'unico modo che non è stato menzionato è il modo in cui lo uso da anni:

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

quindi, nel tuo .profile o .bash_profile o altro, puoi aggiungere:

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


1
Questa risposta l' ha già menzionata ...
heemayl

Sì, ma mi piace il suggerimento per l'alias.
7stud

6

In questa risposta:

  1. C
  2. Pitone
  3. Rubino
  4. Awk alternativo

1. C

Dato che tutti i linguaggi di scripting sono già stati adottati, andrò con C. È abbastanza facile ottenere variabili di ambiente con get_env()funzione (vedere la documentazione della libreria GNU C ). Il resto è semplicemente manipolazione del personaggio

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

Ma anche perché "why not", ecco la versione alternativa di Python tramite argomenti da riga di comando sys.argv

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

3. Rubino

Ruby non viene fornito con Ubuntu per impostazione predefinita, a differenza del compilatore C e dell'interprete Python, ma se mai ti ritrovi a usarlo, la soluzione in Ruby sarebbe questa:

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

Come suggerito da 7stud (Grazie mille!) Nei commenti , anche questo può essere abbreviato

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

e in questo modo

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

4. Awk alternativo

Siamo in grado di utilizzare la split()funzione per suddividere la riga letta in matrice e utilizzare il for-eachciclo per stampare ogni elemento su una riga separata.

awk '{split($0,arr,":"); for(var in arr) print arr[var]}' <<< $PATH

1
Bel esempio di rubini. putsstampa automaticamente gli elementi di un array su linee separate! Puoi anche fare in modo che gli switch eseguano la divisione: ruby -F: -ane 'puts $F' <<<$PATH Spiegazione: -Fimposta $;sul carattere specificato, che è il separatore predefinito usato da String :: split ($; ha un valore predefinito nullo, che si divide su spazi bianchi). -achiama $ _. split, dove $ _ è una riga letta usando get ()) e assegna l'array risultante a $F.
7stud

@ 7stud hey, grazie! :) Non sapevo che ci fosse la -Fbandiera - ho appena iniziato con Ruby, quindi sto facendo le cose in modo leggermente rozzo.
Sergiy Kolodyazhnyy,

No, questa è roba oscura. Il tuo unico liner è molto leggibile e la chiarezza dovrebbe superare ogni volta la brevità.
7stud

Hah. Ho capito uno più corto: ruby -0072 -ne 'puts chomp' <<<$PATH. Spiegazione: -0imposta il separatore del record di input (specificare un carattere in formato ottale; 072 è due punti). Ruby impiega $ _ in modo simile a Perl. Il metodo gets () (usato nel -nciclo) imposta $ _ sulla riga corrente che viene letta. E chomp()senza arg, chomps $ _. Mi piace ancora di più il tuo. :)
7stud

@ 7stud in realtà, quello che hai pubblicato in precedenza, con -Fbandiera, è più breve. Se lo fai echo "ruby -F: -ane 'puts $F' <<<$PATH" |wc -c mi dice che è 271, byte, ma quello con il numero ottale è 276. Questo è per l'intero comando, ovviamente, Se consideriamo che solo il codice stesso puts $Fè chiaramente più corto. :) A proposito, conosci Code Golf ? È il sito per soluzioni per la programmazione di puzzle nel minor numero di byte. C'è una domanda relativa a questa: codegolf.stackexchange.com/q/96334/55572
Sergiy Kolodyazhnyy

4

Abbiamo bisogno di più Java!

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

Salvalo in GetPathByLine.javae compila usando:

javac GetPathByLine.java

Corri 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

Wow, questo è eccessivo. Puoi farlo in una riga in Python 3:python3 -c "import os; [print(p) for p in os.getenv('PATH').split(':')]"
wjandrea il

1
@wjandrea Questo è il punto! : D
Kaz Wolfe,

3

Attraverso awk.

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

Attraverso il pitone.

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

Nota che il rientro è molto importante in Python.


Per un breve periodo:echo $PATH | awk '{gsub(/\:/,"\n");print}'
Sergiy Kolodyazhnyy,

Non sono sicuro del motivo per cui ti preoccupi fileinputquando potresti semplicemente usare input:python3 -c 'print(*input().split(":"), sep="\n")' <<< "$PATH"
wjandrea il

2

Uso "Bash Path Functions" di Stephen Collyer (vedi il suo articolo nel Linux Journal ). Mi permette di usare la "lista separata da due punti" come tipo di dati nella programmazione della shell. Ad esempio, posso produrre un elenco di tutte le directory nella directory corrente:

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

Quindi, listpath -p dirsproduce un elenco.


2

Spiegazione per la risposta di @Cyrus

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

Gli appunti:

Citazioni ANSI-C - spiega $ 'some \ ntext'

Espansione dei parametri della shell : spiega $ {parametro / modello / stringa}, se il modello inizia con '/', tutte le corrispondenze del modello vengono sostituite con stringa.

Quindi abbiamo:

  1. un modello /: che inizia con '/' per sostituire tutte le partite
  2. una stringa $ '\ n' che è quotata con la contrazione $ 'anytext' per trattare il nuovo simbolo di linea (\ n).

1

Un altro modo AWK è quello di trattare ogni directory come un record separato , piuttosto che come un campo separato .

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

Trovo che la sintassi sia particolarmente intuitiva. Ma, se lo desideri, puoi accorciarlo rendendo print $0implicito (è l'azione predefinita e 1restituisce true, facendolo eseguire per ogni riga):

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

Il separatore di record di input e output predefinito di AWK è la nuova riga (interruzione di riga). Impostando il separatore del record di input ( RS) su :prima di leggere l'input, AWK analizza automaticamente un delimitato dai due punti $PATHnei suoi nomi di directory costitutivi. AWK si espande $0per ogni intero record, la nuova riga rimane il separatore del record di output e non gsubè necessario il looping .

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 viene spesso utilizzato per analizzare i record in campi separati, ma non è necessario solo per costruire un elenco di nomi di directory.

Funziona anche con input contenenti spazi (spazi e tabulazioni), anche più spazi consecutivi:

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

Cioè, a meno che tu non induca AWK a ricostruire il record (vedi sotto), non è un problema avere spazi o tabulazioni (i separatori di campo predefiniti) nell'input. PATHProbabilmente il tuo non contiene spazi su un sistema Ubuntu, ma se lo fa, funzionerà comunque.


Vale la pena ricordare, come nota a margine, che la capacità di AWK di interpretare un record come una raccolta di campi diventa utile per il relativo problema di costruzione di una tabella di componenti di directory :

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

Il curioso $1=$1incarico ha lo scopo di costringere AWK a ricostruire il record .

(Ciò è probabilmente più utile nei casi in cui è necessario eseguire ulteriori elaborazioni sui componenti, rispetto all'esempio esatto mostrato semplicemente di stampare la tabella.)



0

Come visualizzare i percorsi in $ PATH separatamente

Questi sono i miei modi preferiti per farlo in base ai miei rispettivi casi d'uso e alle preoccupazioni sulla compatibilità e sull'utilizzo delle risorse.

tr

In primo luogo, se hai bisogno di una soluzione rapida, facile da ricordare e leggibile, fai semplicemente eco PATHe convoglilo per translate ( tr) per trasformare i due punti in nuove righe:

echo $PATH | tr : "\n"

Ha il rovescio della medaglia di usare due processi a causa della pipe, ma se stiamo solo hackerando in un terminale, ci interessa davvero?

Espansione dei parametri della shell di Bash

Se si desidera una soluzione abbastanza permanente in .bashrcuso interattivo, è possibile alias il seguente comando path, ma la leggibilità di questa soluzione è discutibile:

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

Se il motivo inizia con '/', tutte le corrispondenze del motivo vengono sostituite con una stringa. Normalmente viene sostituita solo la prima partita.

Il comando sopra sostituisce i due punti con le nuove righe usando l' espansione dei parametri della shell di Bash :

${parameter/pattern/string}

Per spiegarlo:

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

Buona fortuna a ricordarlo quando stai solo hackerando dalla riga di comando, se non lo hai ancora fatto.

IFS, testato in Bash e Dash

In alternativa, un approccio abbastanza cross-compatibile, leggibile e comprensibile che non si basa su qualcosa di diverso dalla shell è l'uso della seguente funzione (suggerisco nel tuo .bashrc.)

La seguente funzione rende temporaneamente il separatore di campo interno (o input) (IFS) due punti e, quando viene assegnato un array printf, viene eseguito fino a quando l'array non viene esaurito:

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

Questo metodo di creazione funzione , IFSe printfsono fornite da POSIX, così dovrebbe funzionare nella maggior POSIX come gusci (specialmente Dash, che Ubuntu solitamente alias quanti sh).

Pitone

Dovresti usare Python per questo? Potresti. Questo è il comando Python più breve che mi viene in mente per questo:

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

o solo Python 3 (e forse più leggibile?):

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

Dovrebbero funzionare anche in qualsiasi normale ambiente shell, purché tu abbia Python.


0

Questa soluzione è più semplice delle soluzioni 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

Ecco un'altra grande possibilità:

$ 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

Ciò richiederebbe l'installazione di alcune dipendenze:

sudo apt-get install openjdk-8-jdk-headless bsh
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.