dmckee からの提案されたソリューションへの対処:
- Bash の一部のバージョンでは関数名にハイフンを使用できますが、他のバージョン (MacOS X) では使用できません。
- 関数の終了直前に return を使用する必要はないと思います。
- すべてのセミコロンが必要だとは思いません。
- path-element-by-pattern で値をエクスポートする理由がわかりません。グローバル変数を設定する (または作成する) ことと同等と考え
export
てください。これは、可能な限り避けるべきものです。
- あなたが '
replace-path PATH $PATH /usr
' に期待していることはわかりませんが、私が期待するようなことはしません。
以下を含む PATH 値を考えてみましょう。
.
/Users/jleffler/bin
/usr/local/postgresql/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/usr/bin
/bin
/sw/bin
/usr/sbin
/sbin
(' ' から) 得られた結果replace-path PATH $PATH /usr
は次のとおりです。
.
/Users/jleffler/bin
/local/postgresql/bin
/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/local/bin
/bin
/bin
/sw/bin
/sbin
/sbin
/usr は (完全な) パス要素として表示されず、パス要素の一部としてのみ表示されるため、元のパスが返されることを期待していました。
これは、コマンドreplace-path
の 1 つを変更することで修正できます。sed
export $path=$(echo -n $list | tr ":" "\n" | sed "s:^$removestr\$:$replacestr:" |
tr "\n" ":" | sed "s|::|:|g")
「|」の代わりに「:」を使用しました '|' 以降の置換部分を区切る (理論的には) パス コンポーネントに表示される可能性がありますが、PATH の定義により、コロンは表示されません。2 番目の方法でsed
は、現在のディレクトリを PATH の途中から削除できることがわかりました。つまり、PATH の正当な (ひねくれた) 値は次のようになります。
PATH=/bin::/usr/local/bin
処理後、現在のディレクトリは PATH に存在しなくなります。
一致を固定するための同様の変更は、次の場合に適切ですpath-element-by-pattern
。
export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 "^$pat\$")
ちなみに、これは標準ではないことに注意してくださいgrep -m 1
(これは GNU 拡張であり、MacOS X でも利用できます)。実際、-n
オプション forecho
も非標準です。改行をエコーからコロンに変換することによって追加された末尾のコロンを単純に削除する方がよいでしょう。path-element-by-pattern は 1 回だけ使用され、望ましくない副作用 ( と呼ばれる既存のエクスポートされた変数を破壊する$removestr
) があるため、適切にその本体に置き換えることができます。これは、スペースや不要なファイル名の拡張による問題を回避するために引用符をより自由に使用すると、次のようになります。
# path_tools.bash
#
# A set of tools for manipulating ":" separated lists like the
# canonical $PATH variable.
#
# /bin/sh compatibility can probably be regained by replacing $( )
# style command expansion with ` ` style
###############################################################################
# Usage:
#
# To remove a path:
# replace_path PATH $PATH /exact/path/to/remove
# replace_path_pattern PATH $PATH <grep pattern for target path>
#
# To replace a path:
# replace_path PATH $PATH /exact/path/to/remove /replacement/path
# replace_path_pattern PATH $PATH <target pattern> /replacement/path
#
###############################################################################
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 a ":" delimited list to work from (e.g. $PATH)
# $3 the precise string to be removed/replaced
# $4 the replacement string (use "" for removal)
function replace_path () {
path=$1
list=$2
remove=$3
replace=$4 # Allowed to be empty or unset
export $path=$(echo "$list" | tr ":" "\n" | sed "s:^$remove\$:$replace:" |
tr "\n" ":" | sed 's|:$||')
}
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 a ":" delimited list to work from (e.g. $PATH)
# $3 a grep pattern identifying the element to be removed/replaced
# $4 the replacement string (use "" for removal)
function replace_path_pattern () {
path=$1
list=$2
removepat=$3
replacestr=$4 # Allowed to be empty or unset
removestr=$(echo "$list" | tr ":" "\n" | grep -m 1 "^$removepat\$")
replace_path "$path" "$list" "$removestr" "$replacestr"
}
PATH のような変数の問題をデバッグするときに役立つPerl スクリプトがechopath
あります。
#!/usr/bin/perl -w
#
# "@(#)$Id: echopath.pl,v 1.7 1998/09/15 03:16:36 jleffler Exp $"
#
# Print the components of a PATH variable one per line.
# If there are no colons in the arguments, assume that they are
# the names of environment variables.
@ARGV = $ENV{PATH} unless @ARGV;
foreach $arg (@ARGV)
{
$var = $arg;
$var = $ENV{$arg} if $arg =~ /^[A-Za-z_][A-Za-z_0-9]*$/;
$var = $arg unless $var;
@lst = split /:/, $var;
foreach $val (@lst)
{
print "$val\n";
}
}
以下のテスト コードで変更したソリューションを実行すると、次のようになります。
echo
xpath=$PATH
replace_path xpath $xpath /usr
echopath $xpath
echo
xpath=$PATH
replace_path_pattern xpath $xpath /usr/bin /work/bin
echopath xpath
echo
xpath=$PATH
replace_path_pattern xpath $xpath "/usr/.*/bin" /work/bin
echopath xpath
出力は次のとおりです。
.
/Users/jleffler/bin
/usr/local/postgresql/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/usr/bin
/bin
/sw/bin
/usr/sbin
/sbin
.
/Users/jleffler/bin
/usr/local/postgresql/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/work/bin
/bin
/sw/bin
/usr/sbin
/sbin
.
/Users/jleffler/bin
/work/bin
/usr/local/mysql/bin
/Users/jleffler/perl/v5.10.0/bin
/usr/local/bin
/usr/bin
/bin
/sw/bin
/usr/sbin
/sbin
これは私には正しいように見えます-少なくとも、問題が何であるかについての私の定義については。
echopath LD_LIBRARY_PATH
が評価されることに注意してください$LD_LIBRARY_PATH
。あなたの関数がそれを行うことができれば、ユーザーは次のように入力できます:
replace_path PATH /usr/bin /work/bin
これは、次を使用して実行できます。
list=$(eval echo '$'$path)
これにより、コードが次のようにリビジョンされます。
# path_tools.bash
#
# A set of tools for manipulating ":" separated lists like the
# canonical $PATH variable.
#
# /bin/sh compatibility can probably be regained by replacing $( )
# style command expansion with ` ` style
###############################################################################
# Usage:
#
# To remove a path:
# replace_path PATH /exact/path/to/remove
# replace_path_pattern PATH <grep pattern for target path>
#
# To replace a path:
# replace_path PATH /exact/path/to/remove /replacement/path
# replace_path_pattern PATH <target pattern> /replacement/path
#
###############################################################################
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 the precise string to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace_path () {
path=$1
list=$(eval echo '$'$path)
remove=$2
replace=$3 # Allowed to be empty or unset
export $path=$(echo "$list" | tr ":" "\n" | sed "s:^$remove\$:$replace:" |
tr "\n" ":" | sed 's|:$||')
}
# Remove or replace an element of $1
#
# $1 name of the shell variable to set (e.g. PATH)
# $2 a grep pattern identifying the element to be removed/replaced
# $3 the replacement string (use "" for removal)
function replace_path_pattern () {
path=$1
list=$(eval echo '$'$path)
removepat=$2
replacestr=$3 # Allowed to be empty or unset
removestr=$(echo "$list" | tr ":" "\n" | grep -m 1 "^$removepat\$")
replace_path "$path" "$removestr" "$replacestr"
}
次の改訂されたテストも機能するようになりました。
echo
xpath=$PATH
replace_path xpath /usr
echopath xpath
echo
xpath=$PATH
replace_path_pattern xpath /usr/bin /work/bin
echopath xpath
echo
xpath=$PATH
replace_path_pattern xpath "/usr/.*/bin" /work/bin
echopath xpath
以前と同じ出力が生成されます。