ディレクトリがまだ存在しない場合は、$ PATHに追加します


126

誰かが$ PATHにディレクトリを追加するbash関数を書いたことがありますか?

通常、次のようなものを使用してPATHに追加します。

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

.bash_profileにPATHを作成すると、現在のセッションがログインセッションでない限り読み込まれません。これは常に正しいとは限りません。.bashrcにPATHを作成すると、各サブシェルで実行されます。ターミナルウィンドウを起動し、画面を実行してからシェルスクリプトを実行すると、次のようになります。

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

add_to_path()ディレクトリがない場合にのみ追加するというbash関数を作成してみます。しかし、誰かがすでにそのようなことを書いた(または見つけた)場合、私はそれに時間を費やしません。


役立つインフラストラクチャについては、stackoverflow.com / questions / 273909 /…をご覧ください。
dmckee


「まだ存在しない場合にのみ追加」として問題をフレーム化すると、挿入されたアイテムが先頭にあることが重要であるが、そこに巻き込まれない日が来ると、驚くほど驚くでしょう。より良い方法は、要素を挿入してから重複を削除することです。そのため、新しいエントリがすでに存在する場合、効果的に先頭に移動されます。
ドンハッチ

回答:


125

私の.bashrcから:

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

PATHはすでにエクスポート済みとしてマークされているはずなので、再エクスポートする必要はありません。これは、ディレクトリが存在するかどうかを確認してから、ディレクトリを追加する前に、それが気にならないかもしれません。

また、これにより、パスの最後に新しいディレクトリが追加されます。先頭に置くPATH="$1${PATH:+":$PATH"}"には、上記のPATH=行の代わりに使用します。


26
私は気にします。
デニスウィリアムソン

4
@ニール:":$PATH:"ただの代わりに比較するため、動作します"$PATH"
ゴードン

3
@GordonDavisson:おaび申し上げます、私のテストは間違っていて、あなたは正しいです。
ニール

2
@GordonDavisson中括弧内の内容のポイントは何ですか。私はそれを " ${PATH:+"$PATH:"}$ 1"で困惑させることができないようです
ボートコーダー

5
@ Mark0978:bukzorが指摘した問題を修正するために私がやったことです。は、定義されており、空でない値を持っている${variable:+value}かどうかをチェックしvariable、そうである場合、評価の結果を返しvalueます。基本的に、PATHが最初から空白ではない場合、それを"$PATH:$1"; に設定します。空白の場合は、ちょうどに設定します"$1"(コロンがないことに注意してください)。
ゴードンデイヴィソン

19

Gordon Davissonの答えを拡張して、これは複数の引数をサポートします

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

したがって、pathappend path1 path2 path3 ...

付加するために、

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

pathappendと同様に、次のことができます

pathprepend path1 path2 path3 ...


3
これは素晴らしい!ちょっとした変更を加えました。'pathprepend'関数の場合、引数を逆に読み取ると便利です。たとえば、次のように言えば、pathprepend P1 P2 P3で終わることができますPATH=P1:P2:P3。この動作を取得するには、変更for ARG in "$@" dofor ((i=$#; i>0; i--)); do ARG=${!i}
イシュマエル

@ishmaelに感謝、良い提案、私は答えを編集しました。あなたのコメントは2年以上前のものだと思いますが、それ以来戻ってきていません。私は自分の受信箱に着くためにスタック交換メールを取得する方法を理解する必要があります!
ギヨームペロー-Archambault

14

ダグ・ハリスの機能の構造と組み合わされたこの質問に対する私の答えからの何かがここにあります。Bash正規表現を使用します。

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

これは、$1${1}
Andrei

@Andrei:はい、この場合、中括弧は不要です。なぜそれらを含めたのか分かりません。
デニスウィリアムソン

10

選択した回答のコメントにこれを入れますが、コメントはPREフォーマットをサポートしていないようですので、ここに回答を追加してください:

@ gordon-davisson私は不必要な引用と連結の大ファンではありません。3以上のbashバージョンを使用していると仮定すると、代わりにbashのビルトイン正規表現を使用して以下を実行できます。

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

これは、ディレクトリまたはPATHにスペースがある場合を正しく処理します。bashに組み込まれた正規表現エンジンが十分に遅いため、バージョンが行う文字列の連結と補間よりも効率が悪いのではないかという疑問があります。


1
コメントはサポートしformatting using the backtickますが、適切な段落コントロールは取得できません。
ボートコーダー

これにより、追加が最後に追加されます。既存の場所を上書きするには、先頭に追加することが望ましい場合がよくあります。
デニスウィリアムソン14年

@DennisWilliamsonこれは公平なポイントですが、デフォルトの動作としてはお勧めしません。prependの変更方法を理解するのは難しくありません。
クリストファー・スミス14

@ChristopherSmith-re:null unnecessary quoting$PATHはないことを事前に知っていることを意味します。 "$PATH"PATHがnullであるかどうかにかかわらず、OKにします。同様に$1、コマンドパーサーを混乱させる可能性のある文字が含まれている場合。正規表現を引用符で囲むと、"(^|:)$1(:|$)"それができなくなります。
ジェシーチザム

@JesseChisholm:実は、私はクリストファーのポイントはルールが間で異なっていることであると考えている[[]]。私はそれが失敗しない限り、おそらく引用符で囲む必要ができ、すべてを引用することを好むが、私は彼が右だと信じ、および引用符は本当にされていないという必要まわり$PATH。他方、あなたは正しいと私には思えます$1
スコット

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

$ PATHの先頭に$ HOME / binを1回だけ表示し、それ以外の場所に表示する必要がない場合は、代替を受け入れません。


おかげで、それはすてきなエレガントなソリューションですが、私はそれを機能させるためにPATH=${PATH/"...ではなく... する必要があることがわかりましたPATH=${PATH//"
マークブース

ダブルスラッシュ形式は、任意の数の一致と一致する必要があります。単一のスラッシュは最初のものにのみ一致します(bashのマニュアルページで「パターン置換」を検索してください)。なぜそれが機能しなかったのかわからない
...-andybuckley

これ$1は、唯一のエントリ(コロンなし)である異常な場合に失敗します。エントリが2倍になります。
デニスウィリアムソン14年

また、PeterS6gで指摘されているように、積極的に削除します。
デニスウィリアムソン14年

6

冗長な全体を削除するという追加の利点がある代替ソリューションを次に示します。

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

この関数の単一の引数がPATHの前に追加され、同じ文字列の最初のインスタンスが既存のパスから削除されます。つまり、ディレクトリがパスに既に存在する場合、重複として追加されるのではなく、先頭に昇格されます。

この関数は、すべてのエントリの先頭にコロンがあることを確認するためにパスにコロンを追加し、そのエントリを削除して既存のパスに新しいエントリを追加することにより機能します。最後の部分は、bashの${var//pattern/sub}表記法を使用して実行されます。詳細については、bashのマニュアルを参照してください。


良い考え; 欠陥のある実装。すでに/home/robertあなたPATHとあなたがいる場合はどうなるか考えてくださいpathadd /home/rob
スコット

5

これは私のものです(何年も前に私の研究室のシステム管理者であるオスカーによって書かれたと信じています)。必要に応じて、新しいディレクトリを追加または追加できるという利点もあります。

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

使用法:

$ 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

先頭に追加するには、@ Russellのソリューションが好きですが、小さなバグがあります:「/ bin」のようなものを「/ sbin:/ usr / bin:/ var / usr / bin:/ usr / local」のパスに追加しようとすると/ bin:/ usr / sbin」は、「/ bin:」を3回置き換えます(実際にはまったく一致しなかった場合)。そのための修正と@ gordon-davissonの追加ソリューションを組み合わせると、次のようになります。

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
}

4

以下のような単純なエイリアスでトリックを行う必要があります。

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

:文字でパスを分割し、渡された引数と各コンポーネントを比較します。grepは完全な行の一致をチェックし、カウントを出力します。

サンプル使用法:

$ 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

echoコマンドをaddToPathまたは同様のエイリアス/関数に置き換えます。


「grep -x」を使用すると、bash関数に入れたループよりもおそらく高速です。
ダグ・ハリス


2

ここに私がホイップしたものがあります:

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
}

今.bashrcで私は持っています:

add_to_path /usr/local/mysql/bin

オリジナルがスペースを含むディレクトリを処理しない方法についてのコメントに続いて更新されたバージョン(を使用するように指示してくれたこの質問のおかげで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
}

1
任意のディレクトリ名は既に場合、これは失敗する可能性がありPATH、空白が含まれている、*?、または[... ]
スコット

良い点...しかし、私は昔ながらのLinuxの男であり、空白を含むパスを使用することはありません:
ダグ・ハリス

名前に空白文字が含まれているものを作成しないため、あなたにとって良いことです。それらが存在するときにそれらを処理できないコードを書くことにあなたをShaんでください。そして、「昔ながらのLinuxの男」であることはそれと何の関係があるのでしょうか?Windozeはこのアイデアを普及させたかもしれませんが(ありがとう、Documents and SettingsそしてProgram Files)、UnixはWindozeが存在する前から空白を含むパス名をサポートしていました。
スコット

2

まだ誰もこれに言及していないことに少し驚いていますがreadlink -f、相対パスを絶対パスに変換し、PATHに追加するために使用できます。

たとえば、ギヨームペローアルシャンボーの答えを改善するには、

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

になる

pathappend() {
    for ARG in "$@"
    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.基本—これはどのような利点がありますか?

このreadlink -fコマンドは(とりわけ)相対パスを絶対パスに変換します。これにより、次のようなことができます

$ cd / path / to / my / bin / dir
$ pathappend 
$ echo "$ PATH"
<your_old_path>:/ path / to / my / bin / dir

2. PATHに2回入ることをテストする理由

さて、上記の例を考えてみましょう。ユーザーがディレクトリ からもう一度言うと、に なります。もちろん、には存在しません。ただし、は (に相当する絶対パス)に設定されます。これ既にです。だから我々は、追加することを避けるために必要に二度目。pathappend ./path/to/my/bin/dirARG..PATHARGA/path/to/my/bin/dir.PATH/path/to/my/bin/dirPATH

おそらくより重要なことは、その主な目的readlinkは、その名前が示すように、シンボリックリンクを見て、それに含まれる(つまり指す)パス名を読み取ることです。例えば:

$ 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

さて、あなたが言うならpathappend /usr/lib/perl/5.14、あなたはすでに/usr/lib/perl/5.14あなたのPATHにあります、まあ、それは大丈夫です; そのままにしておくことができます。しかし、もし/usr/lib/perl/5.14あなたのPATHにない、我々は呼び出すreadlinkとget ARGA=を/usr/lib/perl/5.14.2、その後、我々は追加していることPATH。しかし、ちょっと待って-あなたがあれば、既に述べpathappend /usr/lib/perl/5.14、その後、あなたはすでに持っている/usr/lib/perl/5.14.2もう一度、あなたのPATHに、そして、我々はそれを追加することを避けるためにそれをチェックする必要がPATH二度目。

3.取引とは何if ARGA=$(readlink -f "$ARG")ですか?

不明な場合、この行はreadlink成功するかどうかをテストします。これは、優れた防御的なプログラミングの実践です。コマンドmの出力コマンド  nの一部 として使用する  場合m  <  n)、コマンドmが失敗したかどうかを確認し、何らかの方法で処理するのが賢明  です。readlink失敗する可能性は低いと思いますが、OS X などから任意のファイルの絶対パスを取得する方法で説明したようにreadlink、GNUの発明です。POSIXでは指定されていないため、Mac OS、Solaris、およびその他のLinux以外のUnixでの可用性は疑わしいです。(実際に、私はちょうど読んコメント「と言いますreadlink -fMac OS X 10.11.6では動作しないように見えrealpathますが、すぐに動作します。」ですので、システムがないreadlink、またはreadlink -f動作しない場所では、これを変更できる可能性があります使用するスクリプトrealpath。)セーフティネットをインストールすることで、コードの移植性をいくらか高めます。

もちろん、持っていないreadlink(または  realpath)システムを使用している場合は、したくないでしょう。pathappend .

2番目の-dテスト([ -d "$ARGA" ])は、おそらく不要です。$ARGディレクトリでreadlink成功する  シナリオは考えられませんが、ディレクトリで$ARGAはありません。私は最初のコピーと貼り付け、if三番目を作成するためのステートメントを、と私は左の  -d怠惰のうち、そこにテストを。

4.他にコメントはありますか?

うん。ここでの他の多くの回答と同様に、これは各引数がディレクトリであるかどうかをテストし、ある場合は処理し、ない場合は無視します。pathappend.」ファイル(.bash_profileやなど.bashrc)やその他のスクリプトでのみ使用している場合、これは適切な場合もあれば、そうでない場合もあります。しかし、この回答が示したように(上記)、インタラクティブに使用することは完全に実行可能です。あなたがする場合、あなたは非常に困惑します

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

コマンドではなくnysql 、私が言ったことに気づきましたか?そして、それは何も言わなかった。間違った引数を静かに無視しただけですか?pathappendmysqlpathappend

上で言ったように、エラーを処理するのは良い習慣です。次に例を示します。

pathappend() {
    for ARG in "$@"
    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
}

(1)引用符を追加する必要がありますreadlink -f "$ARG"。(2)なぜそれが起こるのかはわかりませんが(特に-d "$ARG"テストが成功した後)、readlink失敗するかどうかをテストすることをお勧めします。(3)readlinkシンボリックリンク名を「実際のファイル名」にマッピングするという、主な機能を見落としているようです。(たとえば)/binへのシンボリックリンクである/bin64場合、を繰り返し呼び出すとpathappend /binPATHで言うことができます…:/bin64:/bin64:/bin64:/bin64:…。の新しい値$ARGがに既にあるかどうかを(おそらく)チェックする必要がありPATHます。
スコット

(1)よく観察して、修正しました。(2)どのような場合にreadlinkが失敗しますか?パスが正しく、有効な場所を参照していると仮定します。(3)readlinkの主な機能が何によって決まるのかわかりません。unixファイルシステムのほとんどの(すべてではないにしても)パスは、シンボリックリンクとハードリンクの2種類のリンクに分類できます。重複するパスエントリについては正しいですが、私の答えの目的はそれを追加することではありませんでした(他の回答が既に言及していることに気づいたので)。私はまだ別の答えを追加している唯一の理由は、私はすでに寄与しなかったと思った何か貢献することである
qwertyzw

(2)少なくともコマンドの名前がその目的で暗示/ヒントされている場合、readlinkの 'link'はハードリンクまたはソフトリンクのいずれかを参照できます。しかし、正しい:man readlinkは「readlink-解決されたシンボリックリンクまたは正規のファイル名を印刷する」と言います。この.例では、正規のファイル名と見なすことができます。正しい?
qwertyzw

(1)シンボリックリンクを理解している人にとって、readlinkの名前は明らかにその目的を暗示しています-シンボリックリンクを見て、それに含まれる(つまり指す)パス名を読み取ります。(2)まあ、なぜ失敗するのかわからないと言っreadlink。スクリプトまたは関数に複数のコマンドが含まれていて、コマンドmが失敗した場合(m  <  n)、コマンド  nが壊滅的に失敗する(またはまったく意味をなさない)ことが保証されている場合  、コマンドmが失敗したかどうかを確認し、それを何らかの方法で処理します—少なくとも、…(続き)
スコット

(続き)…スクリプト/関数を診断で中止します。仮に、(a)とのreadlink呼び出しの間に(別のプロセスによって)ディレクトリの名前が変更または削除された場合、または(b)削除された(または破損した)場合、失敗する可能性があります。(3)あなたは私の論点を見失っているようです。他の回答を複製することをお勧めしません。私は、元のかどうかを確認することで、以下のことを言っている(コマンドラインからは)で、すでにあるが、ない新しいのチェックを繰り返す(からの出力を、あなたの答えが不完全なので、間違っています、)。…(続き)testreadlink/usr/bin/readlinkARGPATHARGreadlink
スコット

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

1

この方法はうまくいきます:

if [[ ":$PATH:" != *":/new-directory:"* ]]; then PATH=${PATH}:/new-directory; fi

0

私のバージョンは、ここに投稿されたものよりも空のパスと有効なパスとディレクトリを要求しますが、prepend / append / clean / unique-ify / etcの大規模なコレクションを見つけます。パス操作に役立つシェル関数。現在の状態では、すべてがここにあります:http : //pastebin.com/xS9sgQsX (フィードバックと改善は大歓迎です!)


0

perl one linerを使用できます:

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;' "$@"
}

これは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

Gordon Davissonの答えを少し変更して、何も指定されていない場合は現在のディレクトリを使用します。したがってpadd、PATHに追加するディレクトリから実行するだけです。

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

0

カスタム変数が設定されているかどうかを確認できます。設定されていない場合は設定してから、新しいエントリを追加します。

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

もちろん、これらのエントリは、などの別のスクリプトによって追加された場合でも複製できます/etc/profile


0

このスクリプトを使用すると、次の最後に追加できます$PATH

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

または、次の先頭に追加し$PATHます。

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

これはPOSIX準拠の方法です。

# 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 "$@")"
}

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 "$@"; 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"
        }
}

それはからcribbedさギヨームペロー-アーシャンボウの答えこの質問へmike511の答えここに

更新2017-11-23:@Scottごとのバグを修正


デフォルトで、先頭と末尾(追加)を選択するコマンドラインオプションを提供するため、これを支持しました。しかし、その後、私は考えました:これは説明のない、非常に多くのやや不可解なコードです。(そして、もう一つは、PATHを変更し、その新しい値をエコー、および他のどこに、二つの機能を持っているという事実その出力をキャプチャし、再びPATHに割り当てるには単に不必要な複雑です。)...(続き)
スコット・

(続き)…そして、リンクが間違っていることに気付きました。(そして、なぜあなたはそれらの人を非難しているのか分かりません;あなたは彼らの答えから多くをコピーしていないようです。)そして、私はコードが間違っていたことに気づきました。私はそれがのOK仕事ないと思い維持整形PATHを、それはです保証はありません、すでにあなたは無知を持っている場合は特に、整形式では/etc/profile。PATHに追加しようとしているディレクトリは、すでに複数回存在している可能性があり、コードはその上に詰まっています。…(続き)
スコット

(続き)…たとえば、を先頭に追加しようとする/a/ck/b:/a/ck:/tr:/a/ck、が返されます/a/ck:/b:/a/ck:/tr:/tr:/a/ck
スコット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.