Решение предложенного решения от dmckee:
- Хотя некоторые версии Bash могут разрешать дефисы в именах функций, другие (MacOS X) этого не делают.
- Я не вижу необходимости использовать return непосредственно перед окончанием функции.
- Я не вижу необходимости во всех точках с запятой.
- Я не понимаю, почему у вас есть путь-элемент-по-шаблону для экспорта значения. Считайте
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
, изменив одну из команд sed
:
export $path=$(echo -n $list | tr ":" "\n" | sed "s:^$removestr\$:$replacestr:" |
tr "\n" ":" | sed "s|::|:|g")
Я использовал ':' вместо '|' отделить части замены, так как '|' может (в теории) появляться в компоненте пути, тогда как по определению PATH двоеточие не может. Я заметил, что второй 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
для echo
также нестандартна; Вам было бы лучше просто удалить завершающий двоеточие, которое добавлено путем преобразования новой строки из echo в двоеточие. Так как путь элемент-за-шаблоном используется только один раз, имеет нежелательные побочные эффекты (он забивает любую существующую ранее экспортированную переменную, называемую $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"
}
У меня есть сценарий Perl echopath
, который я считаю полезным при отладке проблем с переменными, подобными PATH:
#!/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
Он выдает тот же вывод, что и раньше.