$ PATHにディレクトリがまだない場合にのみ、ディレクトリを追加するbash関数を記述した人はいますか?
私は通常、次のようなものを使用してPATHに追加します。
_export PATH=/usr/local/mysql/bin:$PATH
_
.bash_profileにPATHを作成した場合、現在のセッションがログインセッションでない限り読み込まれません。これは常に正しいとは限りません。 .bashrcにPATHを作成すると、各サブシェルで実行されます。したがって、ターミナルウィンドウを起動してからscreenを実行してからシェルスクリプトを実行すると、次のようになります。
_$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....
_
add_to_path()
というbash関数を作成してみます。この関数は、ディレクトリがない場合にのみディレクトリを追加します。しかし、誰かがそのようなことをすでに書いた(または見つけた)場合は、それに時間を費やすことはありません。
私の.bashrcから:
pathadd() {
if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
PATH="${PATH:+"$PATH:"}$1"
fi
}
PATHはすでにエクスポート済みとしてマークされている必要があるため、再エクスポートする必要はありません。これは、ディレクトリが存在するかどうか、およびそれがディレクトリであるかどうかを、追加する前にチェックします。
また、これにより、パスの最後に新しいディレクトリが追加されます。最初に置くには、PATH="$1${PATH:+":$PATH"}"
上記の代わりにPATH=
行。
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 ...
my answer から this question までをDoug Harrisの関数の構造と組み合わせたものを以下に示します。 Bash正規表現を使用します。
add_to_path ()
{
if [[ "$PATH" =~ (^|:)"${1}"(:|$) ]]
then
return 0
fi
export PATH=${1}:$PATH
}
これを選択した回答のコメントに入れますが、コメントはPREのフォーマットをサポートしていないようなので、ここに回答を追加します。
@ gordon-davisson私は不必要な引用と連結の大ファンではありません。 bashバージョン> = 3を使用していると仮定すると、代わりにbashの組み込み正規表現を使用して次のことができます。
pathadd() {
if [ -d "$1" ] && [[ ! $PATH =~ (^|:)$1(:|$) ]]; then
PATH+=:$1
fi
}
これは、ディレクトリまたはPATHにスペースがある場合を正しく処理します。 bashの組み込みの正規表現エンジンが遅いので、バージョンが行う文字列の連結と補間よりも効率が悪いのではないかという疑問がありますが、どういうわけか私には美的にきれいに感じられます。
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
}
$ HOME/binが$ PATHの最初に1回だけ出現し、それ以外の場所に出現しないようにする必要がある場合は、代替を受け入れません。
冗長な全体を削除するという追加の利点がある代替ソリューションを次に示します。
function pathadd {
PATH=:$PATH
PATH=$1${PATH//:$1/}
}
この関数への単一の引数がPATHの前に追加され、同じ文字列の最初のインスタンスが既存のパスから削除されます。つまり、ディレクトリがパスにすでに存在する場合、複製として追加されるのではなく、前面に昇格されます。
この関数は、パスの前にコロンを追加してすべてのエントリの前にコロンがあることを確認し、既存のパスの前に新しいエントリを追加して、そのエントリを削除します。最後の部分は、bashの${var//pattern/sub}
表記を使用して実行されます。詳細は bashマニュアル を参照してください。
先頭に付けるには、@ 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
}
これは私のものです(私の昔の研究室のシステム管理者であるOscarが何年も前に書いたものだと思いますが、すべて彼の功績です)。必要に応じて、新しいディレクトリの先頭または末尾に追加できるという追加の利点があります。
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/
以下のような単純なエイリアスでうまくいくはずです。
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または同様のエイリアス/関数に置き換えます。
これが私が打ち上げたものです:
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
}
この質問に対する一連の回答については、StackOverflowで cshでパス変数が重複しないようにする方法 を参照してください。
まだ誰もこれについて言及していないことに少し驚いていますが、readlink -f
を使用して相対パスを絶対パスに変換し、それらをPATHに追加することができます。
たとえば、Guillaume Perrault-Archambaultの答えを改善するには、
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
}
readlink -f
コマンドは、(とりわけ)相対パスを絶対パスに変換します。これにより、次のようなことができます
$ cd /path/to/my/bin/dir $ pathappend 。 $ echo "$ PATH" <your_old_path>:/パス/ to/my/bin/dir
さて、上記の例を考えてみましょう。ユーザーがpathappend .
ディレクトリから/path/to/my/bin/dir
をもう一度言うと、ARG
は.
になります。もちろん、.
はPATH
には存在しません。しかし、ARGA
は/path/to/my/bin/dir
(.
と同等の絶対パス)に設定され、 is PATH
。したがって、/path/to/my/bin/dir
をPATH
に再度追加することを避ける必要があります。
おそらくより重要なこととして、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
と言い、PATHに/usr/lib/Perl/5.14
がすでにある場合は、それで問題ありません。そのままにしておくことができます。ただし、/usr/lib/Perl/5.14
がまだPATHにない場合は、readlink
を呼び出してARGA
= /usr/lib/Perl/5.14.2
を取得し、 that をPATH
に追加します。しかし、少し待ってください— すでにがpathappend /usr/lib/Perl/5.14
と言った場合、PATHに/usr/lib/Perl/5.14.2
がすでにあります。もう一度、PATH
に追加しないように、もう一度確認する必要があります。 。
if ARGA=$(readlink -f "$ARG")
との関係は?不明な場合は、この行でreadlink
が成功するかどうかをテストします。これは、優れた防御的なプログラミング手法です。コマンドの出力 m を part of コマンド n (ここで m << n ))、それは賢明ですコマンド m が失敗したかどうかを確認し、何らかの方法で処理します。 readlink
が失敗する可能性は低いと思います— OS/X などから任意のファイルの絶対パスを取得する方法で説明されているように、readlink
はGNU発明。POSIXでは指定されていないため、Mac OS、Solaris、およびその他のLinux以外のUnixでの利用には疑問がある(実際には、 a comment を読んだだけである) 「readlink -f
はMac OS X 10.11.6では機能しないようですが、realpath
はそのままでは機能します」したがって、readlink
がないシステムを使用している場合、またはreadlink -f
が機能しない場合は、 realpath
を使用するようにこのスクリプトを変更できる場合があります。)セーフティネットをインストールすることで、コードの移植性が多少向上します。
もちろん、readlink
(またはrealpath
)がないシステムを使用している場合は、pathappend .
を実行する必要はありません。
2番目の-d
テスト([ -d "$ARGA" ]
)はおそらく不要です。 $ARG
がディレクトリであり、readlink
は成功するが、$ARGA
がディレクトリではないシナリオは考えられません。最初のif
ステートメントをコピーして貼り付けて3番目のステートメントを作成し、-d
テストを怠惰なままにしました。
うん。ここでの他の多くの回答と同様に、これは各引数がディレクトリであるかどうかをテストし、ディレクトリである場合は処理し、そうでない場合は無視します。 「.
」ファイル(.bash_profile
や.bashrc
など)やその他のスクリプトでのみpathappend
を使用している場合、これは適切な場合とそうでない場合があります。しかし、この回答が示したように(上記)、インタラクティブに使用することは完全に実現可能です。そうするなら、あなたはとても困惑するでしょう
$ pathappend /usr/local/nysql/bin
$ mysql
-bash: mysql: command not found
nysql
ではなくpathappend
コマンドでmysql
と言ったことに気づきましたか?そして、pathappend
は何も言わなかった。それは単に間違った引数を黙って無視しましたか?
上で述べたように、エラーを処理することは良い習慣です。次に例を示します。
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
}
この方法はうまくいきます:
if [[ ":$PATH:" != *":/new-directory:"* ]]; then PATH=${PATH}:/new-directory; fi
function __path_add(){
if [ -d "$1" ] ; then
local D=":${PATH}:";
[ "${D/:$1:/:}" == "$D" ] && PATH="$PATH:$1";
PATH="${PATH/#:/}";
export PATH="${PATH/%:/}";
fi
}
私のバージョンは、空のパスについて注意深くなく、パスが有効であることを主張し、ディレクトリをここに掲載するよりも強く主張していますが、私はprepend/append/clean/unique-ify/etcの大規模なコレクションを見つけました。パス操作に役立つシェル関数。現在の状態の全ロットはここにあります: http://Pastebin.com/xS9sgQsX (フィードバックと改善は大歓迎です!)
以下は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"
}
}
Guillaume Perrault-Archambault の回答 この質問への回答 および mike511 の回答 ここ から引用されます。
更新2017-11-23:@スコットごとのバグを修正
このスクリプトを使用すると、$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
}
カスタム変数が設定されているかどうかを確認できます。それ以外の場合は設定してから、新しいエントリを追加します。
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
などの別のスクリプトによって追加された場合、これらのエントリは重複する可能性があります。
Perlの1つのライナーを使用できます。
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
}
Gordon Davisson's answer を少し修正して、何も指定されていない場合は現在のディレクトリを使用するようにしました。したがって、PATHに追加したいディレクトリからpadd
を実行するだけです。
padd() {
current=`pwd`
p=${1:-$current}
if [ -d "$p" ] && [[ ":$PATH:" != *":$p:"* ]]; then
PATH="$p:$PATH"
fi
}