it-swarm.com.de

Fügen Sie das Verzeichnis zu $ ​​PATH hinzu, falls es noch nicht vorhanden ist

Hat jemand eine Bash-Funktion geschrieben, um $ PATH ein Verzeichnis hinzuzufügen, wenn es nicht bereits vorhanden ist?

Normalerweise füge ich PATH wie folgt hinzu:

export PATH=/usr/local/mysql/bin:$PATH

Wenn ich meinen PATH in .bash_profile konstruiere, wird er nur gelesen, wenn es sich bei der Sitzung, in der ich mich befinde, um eine Anmeldesitzung handelt. Dies ist nicht immer der Fall. Wenn ich meinen PATH in .bashrc konstruiere, wird er mit jeder Subshell ausgeführt. Wenn ich also ein Terminal-Fenster starte und dann screen und dann ein Shell-Skript ausführe, erhalte ich:

$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....

Ich werde versuchen, eine Bash-Funktion namens add_to_path() zu erstellen, die das Verzeichnis nur hinzufügt, wenn es nicht vorhanden ist. Aber wenn jemand schon so etwas geschrieben (oder gefunden) hat, werde ich nicht die Zeit darauf verwenden.

125
Doug Harris

Aus meiner .bashrc:

pathadd() {
    if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
        PATH="${PATH:+"$PATH:"}$1"
    fi
}

Beachten Sie, dass PATH bereits als exportiert markiert sein sollte, sodass ein erneutes Exportieren nicht erforderlich ist. Dies prüft, ob das Verzeichnis existiert & ein Verzeichnis ist, bevor es hinzugefügt wird, was Sie möglicherweise nicht interessiert.

Dadurch wird auch das neue Verzeichnis am Ende des Pfads hinzugefügt. Verwenden Sie am Anfang PATH="$1${PATH:+":$PATH"}" anstelle der obigen PATH=-Zeile.

123
Gordon Davisson

Die Antwort von Gordon Davisson lässt sich auf mehrere Argumente erweitern

pathappend() {
  for ARG in "[email protected]"
  do
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="${PATH:+"$PATH:"}$ARG"
    fi
  done
}

Sie können also pathappend path1 path2 path3 ...

Zum Voranstellen

pathprepend() {
  for ((i=$#; i>0; i--)); 
  do
    ARG=${!i}
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="$ARG${PATH:+":$PATH"}"
    fi
  done
}

Ähnlich wie bei pathappend können Sie dies tun

pathprepend path1 path2 path3 ...

Hier ist etwas von meiner Antwort auf diese Frage kombiniert mit der Struktur von Doug Harris 'Funktion. Es verwendet reguläre Bash-Ausdrücke:

add_to_path ()
{
    if [[ "$PATH" =~ (^|:)"${1}"(:|$) ]]
    then
        return 0
    fi
    export PATH=${1}:$PATH
}
12

Fügen Sie dies in die Kommentare der ausgewählten Antwort ein, aber Kommentare scheinen die PRE-Formatierung nicht zu unterstützen. Fügen Sie die Antwort hier hinzu:

@ Gordon-Davisson Ich bin kein großer Fan von unnötigem Zitieren und Verketten. Angenommen, Sie verwenden eine bash-Version> = 3, können Sie stattdessen die in bash integrierten regulären Ausdrücke verwenden und Folgendes tun:

pathadd() {
    if [ -d "$1" ] && [[ ! $PATH =~ (^|:)$1(:|$) ]]; then
        PATH+=:$1
    fi
}

Dies behandelt Fälle korrekt, in denen sich Leerzeichen im Verzeichnis oder im PATH befinden. Es ist fraglich, ob die in bash eingebaute Regex-Engine langsam genug ist, um weniger effizient zu sein als die String-Verkettung und -Interpolation, die Ihre Version durchführt, aber irgendwie fühlt sie sich für mich nur ästhetisch sauberer an.

10
idempotent_path_prepend ()
{
    PATH=${PATH//":$1"/} #delete any instances in the middle or at the end
    PATH=${PATH//"$1:"/} #delete any instances at the beginning
    export PATH="$1:$PATH" #prepend to beginning
}

Wenn $ HOME/bin genau einmal am Anfang Ihres $ PATH und an keiner anderen Stelle erscheinen soll, akzeptieren Sie keine Ersatzzeichen.

7
Russell

Hier ist eine alternative Lösung, die den zusätzlichen Vorteil hat, redundante Einträge zu entfernen:

function pathadd {
    PATH=:$PATH
    PATH=$1${PATH//:$1/}
}

Das einzige Argument für diese Funktion wird dem Pfad vorangestellt, und die erste Instanz derselben Zeichenfolge wird aus dem vorhandenen Pfad entfernt. Mit anderen Worten, wenn das Verzeichnis bereits im Pfad vorhanden ist, wird es nach vorne verschoben, anstatt als Duplikat hinzugefügt zu werden.

Bei dieser Funktion wird dem Pfad ein Doppelpunkt vorangestellt, um sicherzustellen, dass alle Einträge vorne einen Doppelpunkt haben. Anschließend wird der neue Eintrag dem vorhandenen Pfad vorangestellt, wobei dieser Eintrag entfernt wird. Der letzte Teil wird mit der ${var//pattern/sub}-Notation von bash ausgeführt. Siehe das Bash-Handbuch für weitere Details.

6
Rob Hague

Zum Voranstellen mag ich die Lösung von @ Russell, aber es gibt einen kleinen Fehler: Wenn Sie versuchen, "/ bin" vor einen Pfad von "/ sbin:/usr/bin:/var/usr/bin:/usr/local" zu stellen/bin:/usr/sbin "es ersetzt"/bin: "3 mal (wenn es überhaupt nicht wirklich passt). Wenn ich einen Fix dafür mit der anhängenden Lösung von @ gordon-davisson kombiniere, erhalte ich Folgendes:

path_prepend() {
    if [ -d "$1" ]; then
        PATH=${PATH//":$1:"/:} #delete all instances in the middle
        PATH=${PATH/%":$1"/} #delete any instance at the end
        PATH=${PATH/#"$1:"/} #delete any instance at the beginning
        PATH="$1${PATH:+":$PATH"}" #prepend $1 or if $PATH is empty set to $1
    fi
}
5
PeterS6g

Hier ist meins (ich glaube, es wurde vor Jahren von Oscar geschrieben, dem Systemadministrator meines alten Labors, der ihm alle Ehre macht). Es hat den zusätzlichen Vorteil, dass Sie das neue Verzeichnis wie gewünscht voranstellen oder anhängen können:

pathmunge () {
        if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
           if [ "$2" = "after" ] ; then
              PATH=$PATH:$1
           else
              PATH=$1:$PATH
           fi
        fi
}

Verwendungszweck:

$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /bin/
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /sbin/ after
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin:/sbin/
5
terdon

Ein einfacher Alias ​​wie dieser unten sollte den Trick machen:

alias checkInPath="echo $PATH | tr ':' '\n' | grep -x -c "

Sie müssen lediglich den Pfad des Zeichens: aufteilen und jede Komponente mit dem Argument vergleichen, das Sie übergeben. Grep prüft, ob eine vollständige Zeilenübereinstimmung vorliegt, und gibt die Anzahl aus.

Beispielnutzung:

$ checkInPath "/usr/local"
1
$ checkInPath "/usr/local/sbin"
1
$ checkInPath "/usr/local/sbin2"
0
$ checkInPath "/usr/local/" > /dev/null && echo "Yes" || echo "No"
No
$ checkInPath "/usr/local/bin" > /dev/null && echo "Yes" || echo "No"
Yes
$ checkInPath "/usr/local/sbin" > /dev/null && echo "Yes" || echo "No"
Yes
$ checkInPath "/usr/local/sbin2" > /dev/null && echo "Yes" || echo "No"
No

Ersetzen Sie den Befehl echo durch addToPath oder einen ähnlichen Alias ​​/ eine ähnliche Funktion.

4
nagul

Folgendes habe ich mir ausgedacht:

add_to_path ()
{
    path_list=`echo $PATH | tr ':' ' '`
    new_dir=$1
    for d in $path_list
    do
        if [ $d == $new_dir ]
        then
            return 0
        fi
    done
    export PATH=$new_dir:$PATH
}

Jetzt in .bashrc habe ich:

add_to_path /usr/local/mysql/bin

Aktualisierte Version mit folgendem Kommentar darüber, wie mein Original Verzeichnisse mit Leerzeichen nicht behandelt (dank dieser Frage für Hinweis auf die Verwendung von IFS):

add_to_path ()
{
    new_dir=$1
    local IFS=:
    for d in $PATH
    do
        if [[ "$d" == "$new_dir" ]]
        then
            return 0
        fi
    done
    export PATH=$new_dir:$PATH
}
2
Doug Harris

Eine Reihe von Antworten auf diese Frage finden Sie unter Wie verhindert man das Duplizieren von Pfadvariablen in csh? in StackOverflow.

2

Ich bin ein bisschen überrascht, dass dies noch niemand erwähnt hat, aber Sie können readlink -f verwenden, um relative Pfade in absolute Pfade umzuwandeln und sie als solche zum PATH hinzuzufügen.

Um beispielsweise die Antwort von Guillaume Perrault-Archambault zu verbessern,

pathappend() {
  for ARG in "[email protected]"
  do
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="${PATH:+"$PATH:"}$ARG"
    fi
  done
}

wird

pathappend() {
    for ARG in "[email protected]"
    do
        if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]
        then
            if ARGA=$(readlink -f "$ARG")               #notice me
            then
                if [ -d "$ARGA" ] && [[ ":$PATH:" != *":$ARGA:"* ]]
                then
                    PATH="${PATH:+"$PATH:"}$ARGA"
                fi
            else
                PATH="${PATH:+"$PATH:"}$ARG"
            fi
        fi
    done
}

1. Die Grundlagen - Was nützt das?

Der Befehl readlink -f konvertiert (unter anderem) einen relativen Pfad in einen absoluten Pfad. Dies ermöglicht es Ihnen, etwas zu tun

$ cd /path/to/my/bin/dir[.____.₱$ pathappend .
 $ echo "$ PATH" 
<Ihr alter_Pfad>:/path/to/my/bin/dir

2. Warum testen wir zweimal, ob wir im Pfad sind?

Betrachten Sie das obige Beispiel. Wenn der Benutzer ein zweites Mal pathappend . aus dem Verzeichnis /path/to/my/bin/dir sagt, ist ARG.. Natürlich ist . in PATH nicht vorhanden. Aber dann wird ARGA auf /path/to/my/bin/dir gesetzt (das absolute Pfadäquivalent von .), was bereits in PATH ist. Wir müssen also vermeiden, /path/to/my/bin/dir ein zweites Mal zu PATH hinzuzufügen.

Vielleicht noch wichtiger ist, dass der Hauptzweck von readlink, wie der Name schon sagt, darin besteht, eine symbolische Verknüpfung zu betrachten und den darin enthaltenen Pfadnamen auszulesen (d. H. Darauf zu zeigen). Zum Beispiel:

$ ls -ld /usr/lib/Perl/5.14
-rwxrwxrwx  1   root   root    Sep  3  2015 /usr/lib/Perl/5.14 -> 5.14.2
$ readlink /usr/lib/Perl/5.14
5.14.2
$ readlink -f /usr/lib/Perl/5.14
/usr/lib/Perl/5.14.2

Wenn Sie nun pathappend /usr/lib/Perl/5.14 sagen und bereits /usr/lib/Perl/5.14 in Ihrem PFAD haben, ist das in Ordnung. wir können es einfach so lassen, wie es ist. Wenn /usr/lib/Perl/5.14 jedoch nicht bereits in Ihrem PATH enthalten ist, rufen wir readlink auf und erhalten ARGA = /usr/lib/Perl/5.14.2. Anschließend fügen wir dieseszu PATH hinzu. Aber Moment mal - wenn Sie bereitspathappend /usr/lib/Perl/5.14 gesagt haben, dann haben Sie /usr/lib/Perl/5.14.2 bereits in Ihrem PATH, und wir müssen dies erneut überprüfen, um zu vermeiden, dass es ein zweites Mal zu PATH hinzugefügt wird .

3. Was ist mit if ARGA=$(readlink -f "$ARG") los?

Bei Unklarheiten wird in dieser Zeile geprüft, ob die readlink erfolgreich ist. Dies ist nur eine gute, defensive Programmierpraxis. Wenn wir die Ausgabe des Befehls mals Teil des Befehls verwenden wollen n(wobei m< n) ist es ratsam zu überprüfen, ob der Befehl mist fehlgeschlagen und handhabt das auf irgendeine Weise. Ich denke nicht, dass es wahrscheinlich ist, dass readlink ausfällt - aber wie in Wie man den absoluten Pfad einer beliebigen Datei von OSX und anderswo abruft , readlink ist eine GNU Erfindung. Es ist in POSIX nicht spezifiziert, daher ist seine Verfügbarkeit unter Mac OS, Solaris und anderen Nicht-Linux-Unixen fraglich. Durch die Installation eines Sicherheitsnetzes machen wir unseren Code etwas portabler.

Wenn Sie sich auf einem System ohne readlink befinden, möchten Sie pathappend . natürlich nicht ausführen.

Der zweite -d Test ([ -d "$ARGA" ]) ist wirklich wahrscheinlich unnötig. Ich kann mir kein Szenario vorstellen, in dem $ARG ein Verzeichnis ist und readlink erfolgreich ist, aber $ARGA kein Verzeichnis ist. Ich habe gerade die erste if-Anweisung kopiert und eingefügt, um die dritte zu erstellen, und den -d-Test dort aus Faulheit belassen.

4. Andere Kommentare?

Ja. Wie viele der anderen Antworten hier testet dieses Argument, ob es sich um ein Verzeichnis handelt, verarbeitet es, wenn es vorhanden ist, und ignoriert es, wenn es nicht vorhanden ist. Dies ist möglicherweise ausreichend (oder auch nicht), wenn Sie pathappend nur in "." - Dateien (wie .bash_profile und .bashrc) und anderen Skripten verwenden. Aber wie diese Antwort (oben) gezeigt hat, ist es durchaus machbar, sie interaktiv zu nutzen. Sie werden sehr verwirrt sein, wenn Sie dies tun

$ pathappend /usr/local/nysql/bin
$ mysql
-bash: mysql: command not found

Haben Sie bemerkt, dass ich im Befehl pathappendnysql anstelle von mysql gesagt habe? Und diese pathappend hat nichts gesagt. es einfach stillschweigend das falsche argument ignoriert?

Wie ich bereits sagte, ist es empfehlenswert, mit Fehlern umzugehen. Hier ist ein Beispiel:

pathappend() {
    for ARG in "[email protected]"
    do
        if [ -d "$ARG" ]
        then
            if [[ ":$PATH:" != *":$ARG:"* ]]
            then
                if ARGA=$(readlink -f "$ARG")           #notice me
                then
                    if [[ ":$PATH:" != *":$ARGA:"* ]]
                    then
                        PATH="${PATH:+"$PATH:"}$ARGA"
                    fi
                else
                    PATH="${PATH:+"$PATH:"}$ARG"
                fi
            fi
        else
            printf "Error: %s is not a directory.\n" "$ARG" >&2
        fi
    done
}
2
qwertyzw

Dieser Weg funktioniert gut:

if [[ ":$PATH:" != *":/new-directory:"* ]]; then PATH=${PATH}:/new-directory; fi
1
user2425755
function __path_add(){  
if [ -d "$1" ] ; then  
    local D=":${PATH}:";   
    [ "${D/:$1:/:}" == "$D" ] && PATH="$PATH:$1";  
    PATH="${PATH/#:/}";  
    export PATH="${PATH/%:/}";  
fi  
}  
1
GreenFox

Meine Versionen sind weniger vorsichtig mit leeren Pfaden und bestehen darauf, dass Pfade und Verzeichnisse gültig sind als einige hier veröffentlichte, aber ich finde eine umfangreiche Sammlung von prepend/append/clean/unique-ify/etc. Shell-Funktionen sind nützlich für die Manipulation von Pfaden. Die ganze Menge, in ihrem aktuellen Zustand, ist hier: http://Pastebin.com/xS9sgQsX (Feedback und Verbesserungen sehr willkommen!)

0
andybuckley

Hier ist ein POSIX-konformer Weg.

# USAGE: path_add [include|prepend|append] "dir1" "dir2" ...
#   prepend: add/move to beginning
#   append:  add/move to end
#   include: add to end of PATH if not already included [default]
#          that is, don't change position if already in PATH
# RETURNS:
# prepend:  dir2:dir1:OLD_PATH
# append:   OLD_PATH:dir1:dir2
# If called with no paramters, returns PATH with duplicate directories removed
path_add() {
    # use subshell to create "local" variables
    PATH="$(path_unique)"
    export PATH="$(path_add_do "[email protected]")"
}

path_add_do() {
    case "$1" in
    'include'|'prepend'|'append') action="$1"; shift ;;
    *)                            action='include'   ;;
    esac

    path=":$PATH:" # pad to ensure full path is matched later

    for dir in "[email protected]"; do
        #       [ -d "$dir" ] || continue # skip non-directory params

        left="${path%:$dir:*}" # remove last occurrence to end

        if [ "$path" = "$left" ]; then
            # PATH doesn't contain $dir
            [ "$action" = 'include' ] && action='append'
            right=''
        else
            right=":${path#$left:$dir:}" # remove start to last occurrence
        fi

        # construct path with $dir added
        case "$action" in
            'prepend') path=":$dir$left$right" ;;
            'append')  path="$left$right$dir:" ;;
        esac
    done

    # strip ':' pads
    path="${path#:}"
    path="${path%:}"

    # return
    printf '%s' "$path"
}

# USAGE: path_unique [path]
# path - a colon delimited list. Defaults to $PATH is not specified.
# RETURNS: `path` with duplicated directories removed
path_unique() {
    in_path=${1:-$PATH}
    path=':'

    # Wrap the while loop in '{}' to be able to access the updated `path variable
    # as the `while` loop is run in a subshell due to the piping to it.
    # https://stackoverflow.com/questions/4667509/Shell-variables-set-inside-while-loop-not-visible-outside-of-it
    printf '%s\n' "$in_path" \
    | /bin/tr -s ':' '\n'    \
    | {
            while read -r dir; do
                left="${path%:$dir:*}" # remove last occurrence to end
                if [ "$path" = "$left" ]; then
                    # PATH doesn't contain $dir
                    path="$path$dir:"
                fi
            done
            # strip ':' pads
            path="${path#:}"
            path="${path%:}"
            # return
            printf '%s\n' "$path"
        }
}

Es ist aus Guillaume Perrault-Archambault Antwort auf diese Frage und mike511 s Antwort hier .

UPDATE 23.11.2017: Fehler pro @Scott behoben

0
go2null

Mit diesem Skript können Sie am Ende von $PATH Folgendes hinzufügen:

PATH=path2; add_to_PATH after path1 path2:path3
echo $PATH
path2:path1:path3

Oder füge am Anfang von $PATH hinzu:

PATH=path2; add_to_PATH before path1 path2:path3
echo $PATH
path1:path3:path2

# Add directories to $PATH iff they're not already there
# Append directories to $PATH by default
# Based on https://unix.stackexchange.com/a/4973/143394
# and https://unix.stackexchange.com/a/217629/143394
add_to_PATH () {
  local prepend  # Prepend to path if set
  local prefix   # Temporary prepended path
  local IFS      # Avoid restoring for added laziness

  case $1 in
    after)  shift;; # Default is to append
    before) prepend=true; shift;;
  esac

  for arg; do
    IFS=: # Split argument by path separator
    for dir in $arg; do
      # Canonicalise symbolic links
      dir=$({ cd -- "$dir" && { pwd -P || pwd; } } 2>/dev/null)
      if [ -z "$dir" ]; then continue; fi  # Skip non-existent directory
      case ":$PATH:" in
        *":$dir:"*) :;; # skip - already present
        *) if [ "$prepend" ]; then
           # ${prefix:+$prefix:} will expand to "" if $prefix is empty to avoid
           # starting with a ":".  Expansion is "$prefix:" if non-empty.
            prefix=${prefix+$prefix:}$dir
          else
            PATH=$PATH:$dir  # Append by default
          fi;;
      esac
    done
  done
  [ "$prepend" ] && PATH=$prefix:$PATH
}
0
Tom Hale

Sie können überprüfen, ob eine benutzerdefinierte Variable festgelegt wurde, andernfalls festlegen und dann die neuen Einträge hinzufügen:

if [ "$MYPATHS" != "true" ]; then
    export MYPATHS="true"
    export PATH="$PATH:$HOME/bin:"

    # Java stuff
    export Java_HOME="$(/usr/libexec/Java_home)"
    export M2_HOME="$HOME/Applications/Apache-maven-3.3.9"
    export PATH="$Java_HOME/bin:$M2_HOME/bin:$PATH"

    # etc...
fi

Natürlich könnten diese Einträge immer noch dupliziert werden, wenn sie von einem anderen Skript wie /etc/profile hinzugefügt werden.

0
David Kennedy

Sie können einen Perl One Liner verwenden:

appendPaths() { # append a group of paths together, leaving out redundancies
    # use as: export PATH="$(appendPaths "$PATH" "dir1" "dir2")
    # start at the end:
    #  - join all arguments with :,
    #  - split the result on :,
    #  - pick out non-empty elements which haven't been seen and which are directories,
    #  - join with :,
    #  - print
    Perl -le 'print join ":", grep /\w/ && !$seen{$_}++ && -d $_, split ":", join ":", @ARGV;' "[email protected]"
}

Hier ist es in Bash:

addToPath() { 
    # inspired by Gordon Davisson, http://superuser.com/a/39995/208059
    # call as: addToPath dir1 dir2
    while (( "$#" > 0 )); do
    echo "Adding $1 to PATH."
    if [[ ! -d "$1" ]]; then
        echo "$1 is not a directory.";
    Elif [[ ":$PATH:" == *":$1:"* ]]; then
        echo "$1 is already in the path."
    else
            export PATH="${PATH:+"$PATH:"}$1" # ${x:-defaultIfEmpty} ${x:+valueIfNotEmpty}
    fi
    shift
    done
}
0
dpatru

Ich habe die Antwort von Gordon Davisson leicht geändert , um das aktuelle Verzeichnis zu verwenden, falls keines angegeben ist. Sie können also einfach padd in dem Verzeichnis ausführen, das Sie Ihrem PATH hinzufügen möchten.

padd() {
  current=`pwd`
  p=${1:-$current}
  if [ -d "$p" ] && [[ ":$PATH:" != *":$p:"* ]]; then
      PATH="$p:$PATH"
  fi
}
0
Thorsten Lorenz