如何分别显示$ PATH中的路径


45

我无法弄清楚如何分别列出各种路径$PATH,以便它们看起来像这样:

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

等等

有人知道正确的变量吗?


每个路径都应放在单独的一行上
HankG 2015年

如果您的路径包含换行符,应该打印什么?
Martijn 2015年


Answers:


57

尝试sed

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

tr

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

python

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

在这里,以上所有内容将:用新行替换所有出现的\n


您的python方法的变化:python -c 'import sys;print sys.argv[1].replace(":","\n")' $PATH
Sergiy Kolodyazhnyy

另一个Python变种,有点python -c "print r'$PATH'.replace(':', '\n')"
黑:(

3
tr为我工作(在Mac,btw上)。谢谢。
Mike S.

对于像我这样想将其添加到他们的用户.bash_profile,请像这样添加:alias path='tr ":" "\n" <<< "$PATH"'
Mike S.18年

63

使用bash的参数扩展

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

这将替换所有:$PATH一个换行符(\n),并打印出结果。的内容$PATH保持不变。
如果只想替换第一个:斜杠,请删除第二个斜杠:echo -e "${PATH/:/\n}"


3
我认为这是最好的解决方案,因为它是 bash 完成的。
ryanmjacobs 2015年

1
@ryanmjacobs有点好奇:您认为我的解决方案使用什么?:D
muru 2015年

1
请说明其运作方式。
图兰斯·科尔多瓦

这种操作叫什么?它类似于sed,但不是sed。它叫什么名字,所以我可以在网上搜索有关它的更多信息。
图兰斯·科尔多瓦

3
跟随我答案中的链接(参数扩展)并向下滚动至第二部分,即:${parameter/pattern/string}
Cyrus

27

使用IFS:

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

IFS包含bash进行拆分的字符,因此IFSwith :使bash拆分$PATHon 的扩展:printf在格式字符串上循环参数,直到参数用尽。我们需要使用禁用通配符(通配符扩展),set -f以免PATH目录名称中的通配符被扩展。


2
反斜杠和空格可以正常使用!
heinrich5991 2015年

14

使用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 | xargs -n1 -d: echo多余还是没关系?
Sergiy Kolodyazhnyy 2015年

@Serg echo $PATH | xargs -n1 -d: 将为您做同样的事情,但是您将再使用一个shell。第一个将进行评估echo $PATH并将输出通过管道传递到下一个外壳以完成其余工作。
souravc 2015年

7

这是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

这里还有其他几种方法。我正在使用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手段“将给出脚本后打印每个输入行-e”。该脚本使用替代运算符(s/oldnew/)将所有:行替换为换行符。

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

    -l增加了一个新行给每个print呼叫。在这里,脚本将其输入分割开:,然后遍历每个分割元素并打印出来。

    $ 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)。正则表达式正在寻找在行()或字符的开头跟随非:[^:]+)的范围。这是PCRE的trick俩,意思是“在此之前丢弃所有匹配的东西”,此处也用于避免打印。 ^:\K:

  • 一个cut解决方案(该解决方案在换行符上失败,但可以处理反斜杠和空格):

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

    使用的选项是-d:将输入定界符设置为:-f 1-这意味着打印所有字段(从第一个到最后),--output-delimiter=$'\n'并设置输出定界符。的$'\n'ANSI C报价,并打印在shell换行符的方式。

在以上所有示例中,我在这里使用bash(和其他一些shell)的string<<<)运算符将字符串作为输入传递给程序。所以command <<<"foo"等于echo -n "foo" | command。请注意,我一直在引用"$PATH",如果不使用引号,则shell会吃掉换行符。


@ 7stud注释中提供了另一种方法,它太好了,不包含:

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

那就是所谓的打高尔夫球。的-0指定输入记录分隔符为八进制或十六进制数。这就是定义“行”的内容,其默认值为\n换行符。在这里,我们将其设置:x3a十六进制的a(尝试printf '\x3a\n')。该-l做三件事。首先,它$/从每行的末尾删除输入记录分隔符()-有效地删除了:这里的内容;其次,它将输出记录分隔符($\)设置为给定的任何八进制或十六进制值(012is \n)。如果$\已定义,则将其添加到每个print调用的末尾,因此这将导致在每一个末尾添加一个换行符print

-pe意愿p施加由给出的脚本后RINT每个输入行-e。这里没有脚本,因为所有工作都如上所述由选项标志完成!


您的perl one衬垫还不够晦涩!这是一个版本,其中-e开关没有代码可以执行,因为其他开关可以处理所有内容:perl -0x3a -l012 -pe '' <<<$PATH。说明:-0设置输入记录分隔符(以十六进制/八进制表示,x3A是冒号),-l执行两件事:1)砍掉输入记录分隔符,2)如果指定了一个,则设置输出记录分隔符(以八进制表示) ,012是换行符)。一个-p循环打印出的$ _值,这将是各读入。
7stud

还要注意,您使用的示例-F可以消除-a-n标志,因为-F会自动将其打开:perl -F: -e 'print(join "\n", @F);' <<<$PATH。参见perlrun。很好的例子。
7stud

@ 7stud哇,真是太好了,谢谢!无耻地被盗并添加到我的答案中(我想您不介意,随时将其发布为答案,我会删除它)。并感谢您的-F信息。我已经-F结合使用-an多年了,但是我从来没有意识到一个暗示着其他。顺便说一句,我看到Serg提到了Code Golf,但是如果您喜欢这种事情,我想您也会喜欢Unix&Linux
terdon

嘿,谢谢。关于此语句的一个澄清:最后,-l还将输出记录分隔符添加到每个打印调用的末尾。 实际上,$/如果定义了输出记录分隔符,print总是将输出记录分隔符,添加到字符串的末尾。默认情况下为undef。因此,-ljus设置了$/,这导致print将换行符添加到字符串的末尾。
17st

我想你不介意 - 一点也不。:)
7stud

6

在这个答案中:

  1. C
  2. 蟒蛇
  3. 红宝石
  4. 替代awk

1. C

由于已经使用了所有脚本语言,因此我将使用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

但也因为“为什么不这样做”,这是通过命令行参数的替代python版本 sys.argv

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

3.红宝石

与C编译器和Python解释器不同,Ruby默认情况下不随Ubuntu一起提供,但是如果您发现自己正在使用它,则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

1
漂亮的红宝石示例。 puts自动在单独的行上打印数组的元素!您还可以使开关进行拆分:ruby -F: -ane 'puts $F' <<<$PATH 说明:-F设置$;为指定的字符,这是String :: split使用的默认分隔符($;默认值为nil,在空白处拆分)。 -a调用$ _。split,其中$ _是使用gets()读取的行,并将结果数组分配给$F
7stud

@ 7stud嘿,谢谢!:)不知道有-F标志-我只是从Ruby开始,所以做的事情有些粗略。
Sergiy Kolodyazhnyy

不,这是晦涩的东西。您的一支衬纸非常易读,每次都应简洁明了。
17st

哈哈 我想出一个较短的: ruby -0072 -ne 'puts chomp' <<<$PATH。说明:-0设置输入记录分隔符(指定八进制格式的字符; 072为冒号)。Ruby以类似于Perl的方式使用$ _。gets()方法(在-n循环中使用)将$ _设置为读入的当前。并且chomp()没有arg的情况下,chomps $ _。我还是喜欢你的 :)
7stud

实际上,@ 7stud是您之前发布的带有-F标志的那个较短。如果执行echo "ruby -F: -ane 'puts $F' <<<$PATH" |wc -c 此操作,将告诉我它是271个字节,但是八进制数是276个字节。当然,这适用于整个命令,如果我们认为仅代码本身 puts $F明显较短。:)顺便问一下,您了解Code Golf吗?该站点提供了最短字节数编程难题的解决方案。有一个与此相关的问题:codegolf.stackexchange.com/q/96334/55572
Sergiy Kolodyazhnyy

5

可能没有被提及的唯一方法是我多年来使用它的方法:

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

因此,您可以在.profile或.bash_profile或任何其他内容中添加:

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


1
这个答案提到它已经..
heemayl

是的,但我喜欢使用别名的建议。
17st

4

我们需要更多的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

哇,这太过分了。您可以在Python 3中一行完成此操作:python3 -c "import os; [print(p) for p in os.getenv('PATH').split(':')]"
wjandrea

1
@wjandrea这就是重点!:D
卡兹·沃尔夫

3

通过awk。

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

通过python。

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

请注意,缩进在python中非常重要。


较短的awk:echo $PATH | awk '{gsub(/\:/,"\n");print}'
Sergiy Kolodyazhnyy

我不确定您为什么fileinput只在使用时才会打扰inputpython3 -c 'print(*input().split(":"), sep="\n")' <<< "$PATH"
wjandrea

2

我使用Stephen Collyer的“ Bash Path Functions”(请参阅Linux Journal中他的文章)。它允许我在外壳程序编程中使用“冒号分隔的列表”作为数据类型。例如,我可以通过以下方式生成当前目录中所有目录的列表:

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

然后,listpath -p dirs产生一个列表。


2

@Cyrus答案的说明

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

笔记:

ANSI-C报价 -解释$'some \ ntext'

Shell参数扩展 -解释$ {parameter / pattern / string},如果pattern以'/'开头,则pattern的所有匹配项都替换为string。

因此,我们有:

  1. 模式/:以'/'开头以替换所有匹配项
  2. 字符串$'\ n',以$'anytext'收缩引号引起来以换行符号(\ n)处理。

1

另一种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 重建记录

(对于要对组件进行其他处理的情况,这可能比仅打印表格的确切示例更有用。)



0

如何分别显示$ PATH中的路径

这些是我根据各自的用例以及对兼容性和资源使用的关注而首选的方式。

tr

首先,如果您需要一种快速,易记且易读的解决方案,只需回显PATH并将其通过管道传递给translation(tr),即可将冒号变成换行符:

echo $PATH | tr : "\n"

由于管道的原因,它具有使用两个进程的缺点,但是如果我们只是在终端中进行黑客入侵,我们真的在乎吗?

Bash的shell参数扩展

如果您想要一个永久性的解决方案.bashrc供交互使用,则可以将以下命令别名为path,但是该解决方案的可读性值得怀疑:

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

如果pattern以'/'开头,则pattern的所有匹配项都将替换为字符串。通常只替换第一场比赛。

上面的命令使用Bash的Shell参数扩展将冒号替换为换行符:

${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}
    }

posix提供了这种函数创建IFS和方法printf,因此它应该可以在大多数类似posix的shell中使用(尤其是Dash,Ubuntu通常将其别名为sh)。

蟒蛇

您应该为此使用Python吗?你可以。这是我可以想到的最短的Python命令:

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

或仅适用于Python 3(也许更具可读性?):

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

只要您拥有Python,它们也可以在任何通常的shell环境中工作。


0

该解决方案比JavaCgoawk解决方案简单:

$ 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.