同じパスを複数回追加することなく、システム全体または個々のユーザーのために、$ PATHに物事を追加する方法が必要です。
これを行う理由の1つ.bashrc
は、ログインを必要とせずに追加できるようにするためです。またlightdm
、を呼び出さない(たとえば)を使用するシステムでもより便利です.profile
。
$ PATHから重複を削除する方法に関する質問は承知していますが、重複を削除したくありません。パスがまだ存在しない場合にのみパスを追加する方法が必要です。
同じパスを複数回追加することなく、システム全体または個々のユーザーのために、$ PATHに物事を追加する方法が必要です。
これを行う理由の1つ.bashrc
は、ログインを必要とせずに追加できるようにするためです。またlightdm
、を呼び出さない(たとえば)を使用するシステムでもより便利です.profile
。
$ PATHから重複を削除する方法に関する質問は承知していますが、重複を削除したくありません。パスがまだ存在しない場合にのみパスを追加する方法が必要です。
回答:
追加する新しいパスが次のとおりであると仮定します。
new=/opt/bin
次に、POSIXシェルを使用new
して、パスに既に存在するかどうかをテストし、存在しない場合は追加できます。
case ":${PATH:=$new}:" in
*:"$new":*) ;;
*) PATH="$new:$PATH" ;;
esac
コロンの使用に注意してください。コロンがないと、たとえば、new=/bin
パターンが一致したため、すでにパスにいたと考えるかもしれません/usr/bin
。PATHには通常多くの要素がありますが、PATH内の要素が0と1の特殊なケースも処理されます。最初に要素を持たない(空の)PATHの場合は、空の場合に${PATH:=$new}
割り当てPATH
られるを使用して処理され$new
ます。この方法でパラメーターのデフォルト値を設定することは、すべてのPOSIXシェルの機能です。POSIXドキュメントのセクション2.6.2を参照してください)。
便宜上、上記のコードを関数に追加できます。この関数は、コマンドラインで定義するか、永続的に使用可能にするために、シェルの初期化スクリプトに配置できます(bashユーザーの場合は、次のようになります~/.bashrc
)。
pupdate() { case ":${PATH:=$1}:" in *:"$1":*) ;; *) PATH="$1:$PATH" ;; esac; }
このパス更新機能を使用して、ディレクトリを現在のPATHに追加するには:
pupdate /new/path
PATH
が空の場合、空のエントリ(つまり、現在のディレクトリ)がに追加されますPATH
。別のケースが必要だと思います。
case
。ただやるcase "${PATH:=$new}"
。同様のフォールバックについては、私自身の回答をご覧ください。
/etc/profile.d
と呼ばれるファイルを作成しますmypath.sh
(または、何でも)。lightdmを使用している場合は、それが実行可能であることを確認して/etc/bashrc
ください。さらに、次の機能を追加します。
checkPath () {
case ":$PATH:" in
*":$1:"*) return 1
;;
esac
return 0;
}
# Prepend to $PATH
prependToPath () {
for a; do
checkPath $a
if [ $? -eq 0 ]; then
PATH=$a:$PATH
fi
done
export PATH
}
# Append to $PATH
appendToPath () {
for a; do
checkPath $a
if [ $? -eq 0 ]; then
PATH=$PATH:$a
fi
done
export PATH
}
$ PATHの先頭(先頭に追加)が後続のものよりも優先され、逆に末尾(末尾)にあるものが前のものに優先されます。これは、$ PATHが/usr/local/bin:/usr/bin
ありgotcha
、両方のディレクトリに実行可能ファイル/usr/local/bin
がある場合、デフォルトでその中の1つが使用されることを意味します。
この同じファイル、別のシェル構成ファイル、またはコマンドラインから、次を使用できます。
appendToPath /some/path /another/path
prependToPath /some/path /yet/another/path
これがにある場合.bashrc
、新しいシェルを開始するときに値が複数回表示されるのを防ぎます。先頭に追加されたものを追加する(つまり、$ PATH内でパスを移動する)場合、またはその逆の場合、自分で行う必要があるという制限があります。
$PATH
withを分割することIFS=:
は、最終的にはより柔軟ですcase
。
case
、IMOの一種のハックです。awk
ここでもうまく利用できると思います。
gawk
直接割り当てることができます$PATH
。
次の方法で実行できます。
echo $PATH | grep /my/bin >/dev/null || PATH=$PATH:/my/bin
注:他の変数からPATHを構築する場合、多くのシェルが「」のような「」を解釈するため、それらが空でないことを確認してください。。
-q
grepにはPOSIXが必要ですが、それがそれを持たない(POSIX以外の)grepsがまだあることを意味するかどうかはわかりません。
/my/bin
コードの重要な部分PATH
は、特定のパスが含まれているかどうかを確認することです。
printf '%s' ":${PATH}:" | grep -Fq ":${my_path}:"
すなわち、各パスことを確実PATH
に区切られている両方によって側PATH
セパレータ(:
)は、チェック(-q
か)リテラル文字列(-F
からなる)PATH
セパレータ、パス、および他のPATH
セパレータがそこに存在します。そうでない場合は、パスを安全に追加できます。
if ! printf '%s' ":${PATH-}:" | grep -Fq ":${my_path-}:"
then
PATH="${PATH-}:${my_path-}"
fi
これはPOSIX互換である必要があり、改行文字を含まない任意のパスで動作するはずです。POSIX互換でありながら改行を含むパスを使用したい場合はより複雑ですが、grep
サポート-z
するものがある場合はそれを使用できます。
私は何年もの間、この小さな機能をさまざまな~/.profile
ファイルで持ち歩いてきました。私が働いていたラボのシステム管理者によって書かれたと思いますが、よくわかりません。とにかく、それはゴールディロックのアプローチに似ていますが、わずかに異なります:
pathmunge () {
if ! echo $PATH | /bin/grep -Eq "(^|:)$1($|:)" ; then
if [ "$2" = "after" ] ; then
PATH=$PATH:$1
else
PATH=$1:$PATH
fi
fi
}
したがって、新しいディレクトリをの先頭に追加するにはPATH
:
pathmunge /new/path
そして最後に:
pathmunge /new/path after
/bin/grep
->grep
あなた自身の答えには、それぞれに追加または付加するためのそれぞれの機能があることに気付きました$PATH
。私はそのアイデアが好きでした。そこで、少し引数の処理を追加しました。また、適切に_
名前空間を付けました:
_path_assign() { oFS=$IFS ; IFS=: ; add=$* ; unset P A ; A=
set -- ${PATH:=$1} ; for p in $add ; do {
[ -z "${p%-[AP]}" ] && { unset P A
eval ${p#-}= ; continue ; }
for d ; do [ -z "${d%"$p"}" ] && break
done ; } || set -- ${P+$p} $* ${A+$p}
done ; export PATH="$*" ; IFS=$oFS
}
% PATH=/usr/bin:/usr/yes/bin
% _path_assign \
/usr/bin \
/usr/yes/bin \
/usr/bin/nope \
-P \
/usr/nope/bin \
/usr/bin \
-A \
/nope/usr/bin \
/usr/nope/bin
% echo $PATH
/usr/nope/bin:/usr/bin:/usr/yes/bin:/usr/bin/nope:/nope/usr/bin
デフォルトでは、う-A
にPPEND $PATH
ていますが、この動作を変更することができます-P
追加することにより、repend -P
引数のリスト内の任意の場所を。もう一度-A
渡すことで、保留に戻すことができ-A
ます。
ほとんどの場合、人はの使用を避けることをお勧めしますeval
。しかし、これは永久に使用されている例として際立っていると思います。この場合、表示eval
できるステートメントはP=
or のみA=
です。引数の値は、呼び出される前に厳密にテストされます。これがeval
目的です。
assign() { oFS=$IFS ; IFS=: ; add=$*
set -- ${PATH:=$1} ; for p in $add ; do {
for d ; do [ -z "${d%"$p"}" ] && break
done ; } || set -- $* $p ; done
PATH="$*" ; IFS=$oFS
}
これは、指定した数だけ引数を受け入れ、それぞれに$PATH
一度だけ追加し$PATH
ます。完全に移植可能なPOSIXシェルスクリプトのみを使用し、シェル組み込みのみに依存し、非常に高速です。
% PATH=/usr/bin:/usr/yes/bin
% assign \
/usr/bin \
/usr/yes/bin \
/usr/nope/bin \
/usr/bin \
/nope/usr/bin \
/usr/nope/bin
% echo "$PATH"
> /usr/bin:/usr/yes/bin:/usr/nope/bin:/nope/usr/bin
_
シェル関数をプレフィックスすることで「適切に名前空間化された」という考えはどこから来るのでしょうか?他の言語では、通常、内部グローバル関数(つまり、グローバルである必要があるが、APIの一部として外部で使用することを意図していない関数)を示します。私の名前は確かに素晴らしい選択肢ではありませんが、使用_
するだけでは衝突の問題はまったく解決しないようです-実際の名前空間にタックすることをお勧めします。mikeserv_path_assign()
。
_
場合は、パッケージマネージャーを切り替える必要があります。いずれにせよ、これは本質的には「グローバル、内部、関数」です -宣言されたシェルから呼び出されたすべてのシェルに対してグローバルであり、インタープリターのメモリでハングアウトする解釈された言語スクリプトのほんの一部です。unix.stackexchange.com/questions/120528/...
unset a
プロファイルの最後で(または同等の)ことはできませんか?
見よ!産業用の強力な12行...技術的に bashおよびzshで移植可能なシェル関数は、選択したスタートアップスクリプト~/.bashrc
または~/.zshrc
スタートアップスクリプトを献身的に愛します。
# void +path.append(str dirname, ...)
#
# Append each passed existing directory to the current user's ${PATH} in a
# safe manner silently ignoring:
#
# * Relative directories (i.e., *NOT* prefixed by the directory separator).
# * Duplicate directories (i.e., already listed in the current ${PATH}).
# * Nonextant directories.
+path.append() {
# For each passed dirname...
local dirname
for dirname; do
# Strip the trailing directory separator if any from this dirname,
# reducing this dirname to the canonical form expected by the
# test for uniqueness performed below.
dirname="${dirname%/}"
# If this dirname is either relative, duplicate, or nonextant, then
# silently ignore this dirname and continue to the next. Note that the
# extancy test is the least performant test and hence deferred.
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname}:"* &&
-d "${dirname}" ]] || continue
# Else, this is an existing absolute unique dirname. In this case,
# append this dirname to the current ${PATH}.
PATH="${PATH}:${dirname}"
done
# Strip an erroneously leading delimiter from the current ${PATH} if any,
# a common edge case when the initial ${PATH} is the empty string.
PATH="${PATH#:}"
# Export the current ${PATH} to subprocesses. Although system-wide scripts
# already export the ${PATH} by default on most systems, "Bother free is
# the way to be."
export PATH
}
瞬間的な栄光に備えましょう。次に、これを実行し、願わくばベストを期待するのではなく:
export PATH=$PATH:~/opt/bin:~/the/black/goat/of/the/woods/with/a/thousand/young
代わりにこれを行い、本当にそれを望んでいるかどうかにかかわらず、最高のものを得ることが保証されます:
+path.append ~/opt/bin ~/the/black/goat/of/the/woods/with/a/thousand/young
安全に現在の行に追加して追加することは、${PATH}
一般的に行われている些細なことではありません。便利で一見賢明ですが、フォームのワンライナーはexport PATH=$PATH:~/opt/bin
悪魔的な合併症を招きます:
誤って相対的なディレクトリ名(例:)export PATH=$PATH:opt/bin
。一方でbash
かつzsh
静かに受け入れ、主に相対にdirnamesを無視し、ほとんどの場合、相対的にdirnamesは、いずれかの方法で接頭辞h
またはt
(およびおそらく他の極悪非道な文字)の両方が恥正樹小林さんたala自分自身を不具させる独創1962傑作ハラキリ:
# Don't try this at home. You will feel great pain.
$ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:harakiri && echo $PATH
/usr/local/bin:/usr/bin:arakiri
$ PATH='/usr/local/bin:/usr/bin:/bin' && export PATH=$PATH:tanuki/yokai && echo $PATH
binanuki/yokai # Congratulations. Your system is now face-up in the gutter.
誤ってディレクトリ名が重複しています。重複する${PATH}
ディレクトリ名はほとんど無害ですが、これらは望ましくなく、扱いにくく、やや非効率であり、デバッグ性を妨げ、ドライブの摩耗を促進します。NANDスタイルのSSDは(もちろん)読み取り摩耗の影響を受けませんが、HDDはそうではありません。試行されたコマンドごとに不必要にファイルシステムにアクセスすることは、同じテンポで読み取りヘッドが不必要に消耗することを意味します。その時点で、一見のような無害なワンライナー、ネストされたサブプロセスにネストされたシェルを起動するときに重複が特に油剤ありexport PATH=$PATH:~/wat
、急速には、第七のサークルの中に爆発していない、これはあなたの貴重な子供たちに身を任せるようにしてください。)${PATH}
のような地獄PATH=/usr/local/bin:/usr/bin:/bin:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat:/home/leycec/wat
。それに追加のディレクトリ名を追加した場合に役立つのは、Belzebubbaだけです。(
${PATH}
ディレクトリ名はほとんど無害ですが、通常は望ましくなく、扱いにくく、やや非効率的で、デバッグ性を妨げ、ドライブの摩耗を促進します。エルゴ、上記で定義されたシェル関数のようなフレンドリーな自動化。私たちは自分から自分を救わなければなりません。
disambiguityのために(例えば、現在では外部コマンドを持つ${PATH}
か、システム全体のシェル関数は、他の場所で定義された)、ユーザー定義のシェル関数は、理想的に前置されているかでサポートされているユニークなストリング接尾辞bash
とzsh
、たとえば、のような-しかし、そうでない場合は、標準のコマンドベース名は禁止します+
。
ねえ。できます。私を判断しないでください。
currentに追加する方がcurrentに追加する${PATH}
よりも安全であるため${PATH}
、すべてのものが等しくなります。システム全体のコマンドをユーザー固有のコマンドで上書きすることは、せいぜい不衛生であり、最悪の場合は狂気に陥ることがあります。たとえば、Linuxでは、ダウンストリームアプリケーションは通常、カスタムの非標準の派生物や代替物ではなく、GNU coreutilsのコマンドのバリアントを期待しています。
そうは言っても、そうするための有効なユースケースは絶対にあります。同等の+path.prepend()
関数を定義するのは簡単です。Sans prolix nebulosity、彼と彼女が共有する正気のために:
+path.prepend() {
local dirname
for dirname in "${@}"; do
dirname="${dirname%/}"
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname}:"* &&
-d "${dirname}" ]] || continue
PATH="${dirname}:${PATH}"
done
PATH="${PATH%:}"
export PATH
}
他の場所で受け入れられているGillesの回答は、一般的なケースでは「シェルに依存しないべき等性付加」として印象的に最適です。一般的な場合bash
とzsh
していない望ましくないシンボリックリンク、しかし、パフォーマンスの低下はとても悲しく行うために必要なのGentoo ricerを私の中で。望ましくないシンボリックリンクが存在する場合でも、add_to_PATH()
引数ごとに1つのサブシェルをフォークすることは、シンボリックリンクの重複を挿入する価値があるかどうかは議論の余地があります。
シンボリックリンクの重複さえも排除することを要求する厳しいユースケースの場合、このzsh
特定のバリアントは、非効率的なフォークではなく、効率的なビルトインを介してそうします:
+path.append() {
local dirname
for dirname in "${@}"; do
dirname="${dirname%/}"
[[ "${dirname:0:1}" == '/' &&
":${PATH}:" != *":${dirname:A}:"* &&
-d "${dirname}" ]] || continue
PATH="${PATH}:${dirname}"
done
PATH="${PATH#:}"
export PATH
}
オリジナルでは*":${dirname:A}:"*
なくに注意してください*":${dirname}:"*
。を含む他のほとんどのシェルで:A
は、zsh
残念ながら不在bash
です。引用するにはman zshexpn
:
A:
a
修飾子のようにファイル名を絶対パスに変換し、realpath(3)
ライブラリ関数に結果を渡してシンボリックリンクを解決します。注:realpath(3)
ライブラリー関数を持たないシステムでは、シンボリックリンクは解決されないため、これらのシステムa
でA
は同等です。
どういたしまして。安全な砲撃をお楽しみください。あなたは今、それに値する。
これが私の機能プログラミングスタイルのバージョンです。
*PATH
だけでなく、コロン区切りの変数でも機能しPATH
ます。また注目に値する:
export
ing に関する不可知論者; それは呼び出し元に任されています(例を参照)bash
; フォークなしpath_add(){ #$ 1:指定されたパス文字列に確実に1回だけ含まれる要素 #$ 2:既存のパス文字列値(「PATH」ではなく「$ PATH」) #$ 3(オプション、任意):指定されている場合、$ 1を追加します。それ以外の場合、先頭に追加 # #例: #$ export PATH = $(path_add '/ opt / bin' "$ PATH") #$ CDPATH = $(path_add '/ Music' "$ CDPATH" at_end) local -r already_present = "(^ |:)$ {1}($ | :)" if [["$ 2" =〜$ already_present]]; それから エコー「$ 2」 elif [[$#== 3]]; それから echo "$ {2}:$ {1}" 他に echo "$ {1}:$ {2}" fi }
このスクリプトを使用すると、次の最後に追加できます$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" ] && [ "$prefix" != "" ] && PATH=$prefix:$PATH
}