如何避免在csh中复制路径变量

时间:2008-09-25 20:16:18

标签: path environment-variables csh path-variables

在cshrc文件中通常会有类似的内容来设置路径:

set path = ( . $otherpath $path )

但是,当您多次获取cshrc文件时,路径会重复,您如何防止重复?

编辑:这是一种不干净的方式:

set localpaths = ( . $otherpaths )
echo ${path} | egrep -i "$localpaths" >& /dev/null
if ($status != 0) then
    set path = ( . $otherpaths $path )
endif

12 个答案:

答案 0 :(得分:13)

我很惊讶没有人使用tr ":" "\n" | grep -x技术来搜索$ PATH中是否已存在给定文件夹。有什么理由不去?

一行:

if ! $(echo "$PATH" | tr ":" "\n" | grep -qx "$dir") ; then PATH=$PATH:$dir ; fi

这是一个函数我自己一次向$ PATH添加几个文件夹(使用“aaa:bbb:ccc”表示法作为参数),在添加之前检查每个文件夹是否重复:

append_path()
{
    local SAVED_IFS="$IFS"
    local dir
    IFS=:
    for dir in $1 ; do
        if ! $( echo "$PATH" | tr ":" "\n" | grep -qx "$dir" ) ; then
            PATH=$PATH:$dir
        fi
    done
    IFS="$SAVED_IFS"
}

可以在这样的脚本中调用它:

append_path "/test:$HOME/bin:/example/my dir/space is not an issue"

它具有以下优点:

  • 没有bashisms或任何特定于shell的语法。它与!#/bin/sh(我用短划线测试)
  • 完美搭配
  • 可以一次添加多个文件夹
  • 不排序,保留文件夹顺序
  • 与文件夹名称中的空格完美匹配
  • 无论$ folder是在开头,结尾,中间,还是$ PATH中唯一的文件夹(因此避免测试x:*,*:x,:x: ,x,这里隐含的许多解决方案都是这样做的)
  • 如果$ PATH以“:”开头或结尾,或者其中包含“::”(表示当前文件夹),则工作(并保留)
  • 不需要awksed
  • EPA友好;;保留原始IFS值,所有其他变量都是函数范围的本地变量。

希望有所帮助!

答案 1 :(得分:4)

好吧,不是在csh中,但这就是我在bash中将$ HOME / bin附加到我的路径...

case $PATH in
    *:$HOME/bin | *:$HOME/bin:* ) ;;
    *) export PATH=$PATH:$HOME/bin
esac
季节尝试...

答案 2 :(得分:3)

您可以使用以下Perl脚本修剪重复项的路径。


#!/usr/bin/perl
#
# ^^ ensure this is pointing to the correct location.
#
# Title:    SLimPath
# Author:   David "Shoe Lace" Pyke <eselle@users.sourceforge.net >
#   :   Tim Nelson 
# Purpose: To create a slim version of my envirnoment path so as to eliminate
#       duplicate entries and ensure that the "." path was last.
# Date Created: April 1st 1999
# Revision History:
#   01/04/99: initial tests.. didn't wok verywell at all
#       : retreived path throught '$ENV' call
#   07/04/99: After an email from Tim Nelson <wayland@ne.com.au> got it to
#         work.
#       : used 'push' to add to array
#       : used 'join' to create a delimited string from a list/array.
#   16/02/00: fixed cmd-line options to look/work better
#   25/02/00: made verbosity level-oriented
#
#

use Getopt::Std;

sub printlevel;

$initial_str = "";
$debug_mode = "";
$delim_chr = ":";
$opt_v = 1;

getopts("v:hd:l:e:s:");

OPTS: {
    $opt_h && do {
print "\n$0 [-v level] [-d level] [-l delim] ( -e varname | -s strname | -h )";
print "\nWhere:";
print "\n   -h  This help";
print "\n   -d  Debug level";
print "\n   -l  Delimiter (between path vars)";
print "\n   -e  Specify environment variable (NB: don't include \$ sign)";
print "\n   -s  String (ie. $0 -s \$PATH:/looser/bin/)";
print "\n   -v  Verbosity (0 = quiet, 1 = normal, 2 = verbose)";
print "\n";
        exit;
    };
    $opt_d && do {
        printlevel 1, "You selected debug level $opt_d\n";
        $debug_mode = $opt_d;
    };
    $opt_l && do {
        printlevel 1, "You are going to delimit the string with \"$opt_l\"\n";
        $delim_chr = $opt_l;
    };
    $opt_e && do {
        if($opt_s) { die "Cannot specify BOTH env var and string\n"; }
        printlevel 1, "Using Environment variable \"$opt_e\"\n";
        $initial_str = $ENV{$opt_e};
    };
    $opt_s && do {
        printlevel 1, "Using String \"$opt_s\"\n";
        $initial_str = $opt_s;
    };
}

if( ($#ARGV != 1) and !$opt_e and !$opt_s){
    die "Nothing to work with -- try $0 -h\n";
}

$what = shift @ARGV;
# Split path using the delimiter
@dirs = split(/$delim_chr/, $initial_str);

$dest;
@newpath = ();
LOOP: foreach (@dirs){
    # Ensure the directory exists and is a directory
    if(! -e ) { printlevel 1, "$_ does not exist\n"; next; }
    # If the directory is ., set $dot and go around again
    if($_ eq '.') { $dot = 1; next; }

#   if ($_ ne `realpath $_`){
#           printlevel 2, "$_ becomes ".`realpath $_`."\n";
#   }
    undef $dest;
    #$_=Stdlib::realpath($_,$dest);
    # Check for duplicates and dot path
    foreach $adir (@newpath) { if($_ eq $adir) { 
        printlevel 2, "Duplicate: $_\n";
        next LOOP; 
    }}

    push @newpath, $_;
}

# Join creates a string from a list/array delimited by the first expression
print join($delim_chr, @newpath) . ($dot ? $delim_chr.".\n" : "\n");

printlevel 1, "Thank you for using $0\n";
exit;

sub printlevel {
    my($level, $string) = @_;

    if($opt_v >= $level) {
        print STDERR $string;
    }
}

我希望这很有用。

答案 3 :(得分:2)

我在十年的大部分时间里一直在使用以下(Bourne / Korn / POSIX / Bash)脚本:

:   "@(#)$Id: clnpath.sh,v 1.6 1999/06/08 23:34:07 jleffler Exp $"
#
#   Print minimal version of $PATH, possibly removing some items

case $# in
0)  chop=""; path=${PATH:?};;
1)  chop=""; path=$1;;
2)  chop=$2; path=$1;;
*)  echo "Usage: `basename $0 .sh` [$PATH [remove:list]]" >&2
    exit 1;;
esac

# Beware of the quotes in the assignment to chop!
echo "$path" |
${AWK:-awk} -F: '#
BEGIN   {   # Sort out which path components to omit
            chop="'"$chop"'";
            if (chop != "") nr = split(chop, remove); else nr = 0;
            for (i = 1; i <= nr; i++)
                omit[remove[i]] = 1;
        }
{
    for (i = 1; i <= NF; i++)
    {
        x=$i;
        if (x == "") x = ".";
        if (omit[x] == 0 && path[x]++ == 0)
        {
            output = output pad x;
            pad = ":";
        }
    }
    print output;
}'

在Korn shell中,我使用:

export PATH=$(clnpath /new/bin:/other/bin:$PATH /old/bin:/extra/bin)

这使得PATH包含前面的新目录和其他bin目录,以及主路径值中每个目录名的一个副本,除了旧的和额外的bin目录已经删除了bin。

你必须将它改编为C shell(对不起 - 但我非常相信C Shell Programming Considered Harmful所阐述的真理)。首先,你不必使用冒号分隔符,所以生活实际上更容易。

答案 4 :(得分:2)

好吧,如果您不关心路径的顺序,您可以执行以下操作:

set path=(`echo $path | tr ' ' '\n' | sort | uniq | tr '\n' ' '`)

这将对您的路径进行排序并删除任何相同的额外路径。如果你有 。在你的路径中,你可能想用grep -v删除它并在最后重新添加它。

答案 5 :(得分:2)

这是一个没有分类的长单行:
设置路径=(echo $path | tr ' ' '\n' | perl -e 'while (<>) { print $_ unless $s{$_}++; }' | tr '\n' ' '

答案 6 :(得分:2)

dr_peper,

我通常更喜欢坚持我所生活的shell的脚本功能。使它更具可移植性。所以,我喜欢使用csh脚本编写的解决方案。我只是将它扩展到本地目录中的每个目录,以使其适合我自己。

foreach dir ( $localdirs )
    echo ${path} | egrep -i "$dir" >& /dev/null
    if ($status != 0) then
        set path = ( $dir $path )
    endif
end

答案 7 :(得分:2)

使用sed(1)删除重复项。

$ PATH=$(echo $PATH | sed -e 's/$/:/;s/^/:/;s/:/::/g;:a;s#\(:[^:]\{1,\}:\)\(.*\)\1#\1\2#g;ta;s/::*/:/g;s/^://;s/:$//;')

这将删除第一个实例后的重复项,这可能是您想要的,也可能不是,例如:

$ NEWPATH=/bin:/usr/bin:/bin:/usr/local/bin:/usr/local/bin:/bin
$ echo $NEWPATH | sed -e 's/$/:/; s/^/:/; s/:/::/g; :a; s#\(:[^:]\{1,\}:\)\(.*\)\1#\1\2#g; t a; s/::*/:/g; s/^://; s/:$//;'
/bin:/usr/bin:/usr/local/bin
$

享受!

答案 8 :(得分:1)

以下是我使用的内容 - 也许其他人会发现它很有用:

#!/bin/csh
#  ABSTRACT
#    /bin/csh function-like aliases for manipulating environment
#    variables containing paths.
#
#  BUGS
#    - These *MUST* be single line aliases to avoid parsing problems apparently related
#      to if-then-else
#    - Aliases currently perform tests in inefficient in order to avoid parsing problems
#    - Extremely fragile - use bash instead!!
#
#  AUTHOR
#    J. P. Abelanet - 11/11/10

#  Function-like alias to add a path to the front of an environment variable
#    containing colon (':') delimited paths, without path duplication
#
#  Usage: prepend_path ENVVARIABLE /path/to/prepend
alias prepend_path \
  'set arg2="\!:2";  if ($?\!:1 == 0) setenv \!:1 "$arg2";  if ($?\!:1 && $\!:1 !~ {,*:}"$arg2"{:*,}) setenv \!:1 "$arg2":"$\!:1";'

#  Function-like alias to add a path to the back of any environment variable 
#    containing colon (':') delimited paths, without path duplication
#
#  Usage: append_path ENVVARIABLE /path/to/append
alias append_path \
  'set arg2="\!:2";  if ($?\!:1 == 0) setenv \!:1 "$arg2";  if ($?\!:1 && $\!:1 !~ {,*:}"$arg2"{:*,}) setenv \!:1 "$\!:1":"$arg2";'

答案 9 :(得分:0)

我总是在.cshrc中从头开始设置我的路径。 那是我从一个基本路径开始,如:

set path = (. ~/bin /bin /usr/bin /usr/ucb /usr/bin/X11)

(取决于系统)。

然后做:

set path = ($otherPath $path)

添加更多内容

答案 10 :(得分:0)

我和原始问题有同样的需要。 基于您以前的答案,我在Korn / POSIX / Bash中使用过:

export PATH=$(perl -e 'print join ":", grep {!$h{$_}++} split ":", "'$otherpath:$PATH\")

我很难直接在csh中翻译它(csh转义规则很疯狂)。我曾经使用过(正如dr_pepper建议的那样):

set path = ( `echo $otherpath $path | tr ' ' '\n' | perl -ne 'print $_ unless $h{$_}++' | tr '\n' ' '`)

您是否有更多简化(减少管道数量)的想法?

答案 11 :(得分:0)

在csh中设置路径(小写的csh变量)而不是PATH(环境变量)时,可以使用set -f和set -l,这将只保留每个列表元素的一次出现(最好保留其中一个分别是第一个或最后一个)。

https://nature.berkeley.edu/~casterln/tcsh/Builtin_commands.html#set

像这样

cat foo.csh # or .tcshrc or whatever: set -f path = (/bin /usr/bin . ) # initial value set -f path = ($path /mycode /hercode /usr/bin ) # add things, both new and duplicates

每次source时,都不会继续使用重复扩展PATH:

% source foo.csh % echo $PATH % /bin:/usr/bin:.:/mycode:/hercode % source foo.csh % echo $PATH % /bin:/usr/bin:.:/mycode:/hercode

set -f在那里确保仅保留每个PATH元素的第一次出现。