Как отобразить пути в $PATH отдельно

Я не могу понять, как перечислить различные пути в $PATH отдельно, чтобы они выглядели так:

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

и т.п.

Кто-нибудь знает правильную переменную для этого?

16 ответов

Решение

Пытаться sed:

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

Или же tr:

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

Или же python:

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

Здесь все вышеперечисленное заменит все вхождения : с новыми линиями \n,

Используйте расширение параметров bash:

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

Это заменяет все : в $PATH по новой строке (\n) и печатает результат. Содержание $PATH остается неизменной.
Если вы хотите заменить только первый :, удалите вторую косую черту: echo -e "${PATH/:/\n}"

Используя IFS:

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

IFS содержит символы, по которым bash выполняет разбиение, поэтому IFS с : делает Bash сплит расширение $PATH на :, printf Зацикливает аргументы над строкой форматирования, пока аргументы не будут исчерпаны. Нам нужно отключить глобирование (расширение по шаблону), используя set -f так что подстановочные знаки в именах каталогов PATH не раскрываются.

С помощью xargs:

xargs -n1 -d: <<< $PATH

От man xargs

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

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

Вероятно, единственный способ, который не был упомянут, это то, как я использовал его годами:

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

Итак, в ваш.profile или.bash_profile или что-то еще вы можете добавить:

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

В этом ответе:

  1. С
  2. питон
  3. Рубин
  4. Альтернативный awk

1. С

Поскольку все языки сценариев уже заняты, я перейду к C. Довольно просто получить переменные среды с get_env() функция (см. документацию библиотеки GNU C). Остальное - просто манипуляция персонажем.

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 через аргументы командной строки sys.argv

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

3. Рубин

Ruby не поставляется с Ubuntu по умолчанию, в отличие от компилятора C и интерпретатора Python, но если вы когда-нибудь будете его использовать, решение в Ruby будет таким:

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

Как предлагает 7stud (спасибо большое!) В комментариях, это также можно сократить с помощью

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

и так

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

4. Альтернативный awk

Мы можем использовать split() функция, чтобы разбить строку, прочитанную в массив, и использовать for-each цикл для распечатки каждого элемента в отдельной строке.

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

Вот эквивалент в 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

Вот еще несколько подходов. Я использую PATH с каталогами, содержащими обратную косую черту, пробелы и даже новую строку, чтобы показать, что они должны работать с чем угодно (кроме cut тот, который терпит неудачу на новых строках):

$ echo "$PATH"
/bin:usr/bin/:/usr/local/bin:/some\ horrible thing:/even 
new lines
  • Некоторые способы Perl:

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

    -p означает "печатать каждую строку ввода после применения скрипта, заданного -eMsgstr "Сценарий использует оператор подстановки (s/oldnew/) заменить все : с символами новой строки.

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

    -l добавляет новую строку к каждому print вызов. Здесь скрипт разделяет свой вход на : а затем зацикливается на каждом элементе split и печатает его.

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

    -a марки perl вести себя как awk: он разделит каждую из своих строк ввода на символ, заданный -F (так :и сохраняем результат в массиве @F, $" это специальная переменная Perl, "разделитель списка", значение которой печатается между каждым элементом печатного списка. Так что установка его на новую строку сделает print @list распечатать каждый элемент @list а затем перевод строки. Здесь мы используем его для печати @F,

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

    Та же идея, что и выше, только меньше в гольфе. Вместо того, чтобы использовать $"мы явно joinСоздание массива с символами новой строки, а затем печать.

  • просто grep с магией PCRE:

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

    -o марки grep печатать только совпадающую часть каждой строки, поэтому каждое совпадение печатается на отдельной строке. -P включает Perl-совместимые регулярные выражения (PCRE). Регулярное выражение ищет отрезки не: ([^:]+), которые следуют либо за началом строки (^) или : персонаж. \K это трюк PCRE, который означает "отбросить что-либо подходящее до этого момента" и используется здесь, чтобы избежать печати : также.

  • И cut решение (это не работает на новых строках, но может иметь дело с обратными слешами и пробелами):

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

    Используемые опции -d: который устанавливает входной разделитель в :, -f 1- что означает печать всех полей (от 1-го до конца) и --output-delimiter=$'\n' который устанавливает, ну, выходной разделитель. $'\n' ANSI C цитирование и способ печати символа новой строки в оболочке.

Во всех приведенных выше примерах я использую строку bash (и некоторых других оболочек) здесь (<<<) оператор для передачи строки в качестве ввода в программу. Так command <<<"foo"эквивалентно echo -n "foo" | command, Обратите внимание, что я всегда цитирую"$PATH"без кавычек оболочка съела бы символ новой строки.


@ 7stud дал другой подход в комментариях, который слишком хорош, чтобы не включать:

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

Это то, что известно как гольф. -0 определяет входной разделитель записей в виде восьмеричного или шестнадцатеричного числа. Это то, что определяет "линию" и ее значение по умолчанию \n, символ новой строки. Здесь мы устанавливаем это :который x3a в шестнадцатеричном (попробуйтеprintf '\x3a\n'). -l делает три вещи. Сначала он удаляет разделитель входной записи ($/) с конца каждой строки - эффективно удаляя: здесь - и во-вторых, он устанавливает разделитель выходной записи ($\) любому восьмеричному или шестнадцатеричному значению, которое ему дается (012 является \n). Если $\определяется, он добавляется в конце каждогоprintвызов, так что это приведет к добавлению новой строки к каждомуprint,

-peбудет печатать каждую строку ввода после применения скрипта, заданного -e, Здесь нет сценария, потому что вся работа выполняется с помощью флагов, как описано выше!

Нам нужно больше Java!

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

Сохранить это в GetPathByLine.javaи скомпилируйте, используя:

javac GetPathByLine.java

Бежать с:

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

Через awk.

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

Через питона.

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

Обратите внимание, что отступы очень важны в Python.

Я использую "Функции пути Bash" Стивена Коллайера (см. Его статью в Linux Journal). Это позволяет мне использовать "список через двоеточие" в качестве типа данных в программировании оболочки. Например, я могу создать список всех каталогов в текущем каталоге:

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

Затем, listpath -p dirs производит список.

Объяснение для ответа @Cyrus

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

Заметки:

Цитирование ANSI-C - объясняет $'some\ntext'

Расширение параметра оболочки - оно объясняет ${параметр / шаблон / строку}. Если шаблон начинается с '/', все совпадения шаблона заменяются строкой.

Итак, мы имеем:

  1. шаблон /: который начинается с '/', чтобы заменить все совпадения
  2. строка $'\n', которая заключена в сокращение $ anytext для обработки символа новой строки (\n).
jq -Rr 'gsub(":";"\n")' <<<$PATH

Другой способ AWK - рассматривать каждый каталог как отдельную запись, а не как отдельное поле.

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

Я нахожу этот синтаксис особенно интуитивным. Но, если хотите, вы можете сократить его, сделав print $0 неявное (это действие по умолчанию, и 1 оценивается как true, в результате чего это делается для каждой строки):

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

По умолчанию в качестве разделителя входных и выходных записей AWK используется перевод строки. Установив разделитель входной записи (RS) чтобы : перед прочтением ввода AWK автоматически анализирует разделенный двоеточиями $PATH в составные имена каталогов. AWK расширяется $0 для каждой записи в целом, символ новой строки остается разделителем выходной записи, без зацикливания или gsub нужно.

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 часто используется для анализа записей в отдельных полях, но для этого не нужно просто составлять список имен каталогов.

Это работает даже для ввода, содержащего пробелы (пробелы и табуляции), даже несколько последовательных пробелов:

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

То есть, если вы не заставите AWK перестроить запись (см. Ниже), нет проблем с наличием пробелов или табуляций (разделителей полей по умолчанию) на входе. Ваш PATH вероятно, не содержит пробелов в системе Ubuntu, но если это так, это все равно будет работать.


В качестве дополнительного примечания стоит упомянуть, что способность AWK интерпретировать запись как набор полей становится полезной для связанной проблемы построения таблицы компонентов каталога:

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

Любопытный $1=$1 назначение служит для того, чтобы заставить AWK перестроить запись.

(Это, вероятно, более полезно для случаев, когда для компонентов требуется дополнительная обработка, чем для показанного точного примера простой печати таблицы.)

Как отобразить пути в $PATH отдельно

Это мои предпочтительные способы сделать это, основываясь на моих соответствующих сценариях использования и заботах о совместимости и использовании ресурсов.

tr

Во-первых, если вам нужно быстрое, легко запоминающееся и читаемое решение, просто откройте эхо PATH и трубку это перевести ( tr ) превратить двоеточия в новые строки:

echo $PATH | tr : "\n"

Недостатком является использование двух процессов из-за конвейера, но если мы просто взломали терминал, нас это действительно волнует?

Расширение параметров оболочки Bash

Если вы хотите довольно постоянное решение в вашем .bashrc для интерактивного использования, вы можете псевдоним следующей команды path, но читаемость этого решения сомнительна:

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

Если шаблон начинается с '/', все совпадения шаблона заменяются строкой. Обычно заменяется только первый матч.

Приведенная выше команда заменяет двоеточие символами новой строки, используя расширение параметров оболочки Bash:

${parameter/pattern/string}

Чтобы объяснить это:

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

Удачи, помня об этом, когда вы просто взламываете командную строку, если вы еще не добавили псевдоним.

IFS, проверено в Bash и Dash

В качестве альтернативы, достаточно кросс-совместимый, читаемый и понятный подход, который не зависит ни от чего, кроме оболочки, использует следующую функцию (я предлагаю в вашей .bashrc.)

Следующая функция временно делает Внутренний (или Входной) Разделитель Полей (IFS) двоеточием, и когда массив передается printf, он выполняется, пока массив не будет использован:

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

Этот метод создания функции, IFS, а также printf предоставляются posix, поэтому он должен работать в большинстве posix-подобных оболочек (особенно в Dash, которые в Ubuntu обычно используют как sh).

питон

Вы должны использовать Python для этого? Вы могли бы. Это самая короткая команда Python, о которой я могу подумать:

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

или только Python 3 (и, возможно, более читабельный?):

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

Они также должны работать в любой обычной оболочке, если у вас есть Python.

Это решение проще, чем Java, C, go и awk:

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

Вот еще одна отличная возможность:

$ 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

Это потребует установки некоторых зависимостей:

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

Это работает в Git Bash для Windows:

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

Он красиво разбивает строки:

c:\Rtools\mingw_32\bin
C:\Program Files (x86)\Intel\MPI-RT\4.1.3.045\ia32\bin
C:\Windows\system32
C:\Windows
C:\Windows\System32\Wbem
C:\Windows\System32\WindowsPowerShell\v1.0\
C:\ProgramData\chocolatey\bin
C:\Program Files (x86)\vim\vim74
Другие вопросы по тегам