Как я управляю элементами $PATH в сценариях оболочки?

Многие объяснения уже присутствуют, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать рекомендациям, чтобы избежать NullPointerException вообще.

См. также: A хороший список лучших практик

Я бы добавил, очень важно, хорошо использовать модификатор final. Использование "окончательной" модификатор, когда это применимо в Java

Сводка:

  1. Используйте модификатор final для обеспечения хорошей инициализации.
  2. Избегайте возврата null в методы, например, при возврате пустых коллекций.
  3. Использовать аннотации @NotNull и @Nullable
  4. Быстрое завершение работы и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, когда они не должен быть пустым.
  5. Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитают valueOf() поверх toString ().
  7. Используйте null safe StringUtils StringUtils.isEmpty(null).

30
задан Community 23 May 2017 в 12:18
поделиться

8 ответов

Обращение к предлагаемому решению от dmckee:

  1. , В то время как некоторые версии Bash могут позволить дефисы на имена функций, другие (MacOS X) не делают.
  2. я не вижу потребность сразу использовать возврат перед концом функции.
  3. я не вижу потребность во всех точках с запятой.
  4. я не вижу, почему у Вас есть экспорт path-element-by-pattern значение. Думайте export как эквивалентные установке (или даже создание) глобальная переменная - что-то, чтобы избежаться, когда это возможно.
  5. я не уверен, что Вы ожидаете' replace-path PATH $PATH /usr' делать, но это не делает то, что я ожидал бы.

Рассматривают значение ПУТИ, которое начинается содержащий:

.
/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 не появляется как (полный) элемент пути, только как часть элемента пути.

Это может быть зафиксировано в [1 116] путем изменения одного из sed команды:

export $path=$(echo -n $list | tr ":" "\n" | sed "s:^$removestr\$:$replacestr:" |
               tr "\n" ":" | sed "s|::|:|g")

я использовал ':' вместо '|' для разделения частей замены с тех пор '|' мог (в теории), появляются в компоненте контура, тогда как по определению ПУТИ, двоеточие не может. Я замечаю, что второе sed могло устранить текущий каталог с середины ПУТИ. Таким образом, законное (хотя извращенный) значение ПУТИ могло быть:

PATH=/bin::/usr/local/bin

После обработки, текущий каталог больше не был бы на ПУТИ.

А подобное изменение для привязки соответствия является соответствующим в [1 119]:

export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 "^$pat\$")

я отмечаю мимоходом, что grep -m 1 не является стандартным (это - расширение GNU, также доступное на MacOS X). И, действительно, -n опция для [1 122] также нестандартна; Вы были бы более обеспечены просто удаление запаздывающего двоеточия, которое добавляется на основании преобразования новой строки от эха в двоеточие. Так как path-element-by-pattern используется только однажды, имеет нежелательные побочные эффекты (он ударяет экспортируемую переменную любого существования ранее, названную $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_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 ' 

следующий пересмотренный тест теперь работает также:

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

Это производит тот же вывод как прежде.

$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 '

следующий пересмотренный тест теперь работает также:

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

Это производит тот же вывод как прежде.

$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

Это производит тот же вывод как прежде.

$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 ' 

следующий пересмотренный тест теперь работает также:

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

Это производит тот же вывод как прежде.

$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 '

следующий пересмотренный тест теперь работает также:

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

Это производит тот же вывод как прежде.

$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

Это производит тот же вывод как прежде.

19
ответ дан Jonathan Leffler 28 November 2019 в 00:14
поделиться

Перерегистрация моего ответа на , Что самый изящный путь состоит в том, чтобы удалить путь из переменной $PATH в Bash? :

#!/bin/bash
IFS=:
# convert it to an array
t=($PATH)
unset IFS
# perform any array operations to remove elements from the array
t=(${t[@]%%*usr*})
IFS=:
# output the new array
echo "${t[*]}"

или острота:

PATH=$(IFS=':';t=($PATH);unset IFS;t=(${t[@]%%*usr*});IFS=':';echo "${t[*]}");
7
ответ дан Community 28 November 2019 в 00:14
поделиться

Для удаления элемента можно использовать sed:

#!/bin/bash
NEW_PATH=$(echo -n $PATH | tr ":" "\n" | sed "/foo/d" | tr "\n" ":")
export PATH=$NEW_PATH

удалит пути, которые содержат "нечто" от пути.

Вы могли также использовать sed для вставки новой строки прежде или после данной строки.

Редактирование: можно удалить дубликаты путем передачи по каналу через вид и uniq:

echo -n $PATH | tr ":" "\n" | sort | uniq -c | sed -n "/ 1 / s/.*1 \(.*\)/\1/p" | sed "/foo/d" | tr "\n" ":"
3
ответ дан florin 28 November 2019 в 00:14
поделиться

Существует несколько соответствующих программ в ответах на" , Как удержаться от дублирования переменной пути в csh". Они концентрируются больше на обеспечении, что нет никаких повторных элементов, но сценарий, который я предоставляю, может использоваться как:

export PATH=$(clnpath $head_dirs:$PATH:$tail_dirs $remove_dirs)

Принятие у Вас есть один или несколько каталогов в $head_dirs и один или несколько каталогов в $tail_dirs и один или несколько каталогов в $remove_dirs, тогда это использует оболочку для конкатенации головы, текущей и части хвоста в крупное значение, и затем удаляет каждый из каталогов, перечисленных в $remove_dirs от результата (не ошибка, если они не существуют), а также устранение вторых и последующих случаев любого каталога в пути.

Это не обращается к компонентам контура помещения в определенное положение (кроме вначале или конец и те только косвенно). Письменным образом определение, где Вы хотите добавить новый элемент, или какой элемент Вы хотите заменить, грязно.

2
ответ дан Community 28 November 2019 в 00:14
поделиться

Просто примечание, которые колотят себя, может сделать поиск и замену. Это может сделать все нормальное "однажды или все", случаи [в] чувствительных опциях Вы ожидали бы.

Из страницы справочника:

$ {параметр/шаблон/строка}

шаблон расширен для создания шаблона так же, как в расширении пути. Параметр расширен, и самое долгое соответствие шаблона против его значения заменяется строкой. Если Ipattern начинается/, все соответствия шаблона заменяются строкой. Обычно только первое соответствие заменяется. Если шаблон начинается с #, он должен соответствовать в начале расширенного значения параметра. Если шаблон начинается с %, он должен соответствовать в конце расширенного значения параметра. Если строка является пустой, соответствия шаблона удалены, и / после шаблона может быть опущен. Если параметр или *, операция замены применяется к каждому позиционному параметру в свою очередь, и расширение является результирующим списком. Если параметр является переменной типа массив, преобразованной в нижний индекс с или *, операция замены применяется к каждому члену массива в свою очередь, и расширение является результирующим списком.

можно также сделать полевое разделение путем установки $IFS (разделитель поля ввода) к желаемому разделителю.

2
ответ дан dj_segfault 28 November 2019 в 00:14
поделиться

Хорошо, благодаря всем респондентам. Я подготовил инкапсулированную версию ответа флорина. Первая передача похожа на это:

# 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
#    
###############################################################################
# Finds the _first_ list element matching $2
#
#    $1 name of a shell variable to be set
#    $2 name of a variable with a path-like structure
#    $3 a grep pattern to match the desired element of $1
function path-element-by-pattern (){ 
    target=$1;
    list=$2;
    pat=$3;

    export $target=$(echo -n $list | tr ":" "\n" | grep -m 1 $pat);
    return
}

# Removes or replaces an element of $1
#
#   $1 name of the shell variable to set (i.e. PATH) 
#   $2 a ":" delimited list to work from (i.e. $PATH)
#   $2 the precise string to be removed/replaced
#   $3 the replacement string (use "" for removal)
function replace-path () {
    path=$1;
    list=$2;
    removestr=$3;
    replacestr=$4; # Allowed to be ""

    export $path=$(echo -n $list | tr ":" "\n" | sed "s|$removestr|$replacestr|" | tr "\n" ":" | sed "s|::|:|g");
    unset removestr
    return 
}

# Removes or replaces an element of $1
#
#   $1 name of the shell variable to set (i.e. PATH) 
#   $2 a ":" delimited list to work from (i.e. $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=$2;
    removepat=$3; 
    replacestr=$4; # Allowed to be ""

    path-element-by-pattern removestr $list $removepat;
    replace-path $path $list $removestr $replacestr;
}

Все еще обнаружение ошибок потребностей во всех функциях, и я должен, вероятно, всунуть повторное решение для пути, в то время как я в нем.

Вы используете его путем выполнения . /include/path/path_tools.bash в рабочем сценарии и обращения replace-path* функции.

<час>

я все еще открыт для новых и/или лучших ответов.

1
ответ дан dmckee 28 November 2019 в 00:14
поделиться

Это - легкое использование awk.

Замена

{
  for(i=1;i<=NF;i++) 
      if($i == REM) 
          if(REP)
              print REP; 
          else
              continue;
      else 
          print $i; 
}

Запускается, она с помощью [1 114]

function path_repl {
    echo $PATH | awk -F: -f rem.awk REM="$1" REP="$2" | paste -sd:
}

$ echo $PATH
/bin:/usr/bin:/home/js/usr/bin
$ path_repl /bin /baz
/baz:/usr/bin:/home/js/usr/bin
$ path_repl /bin
/usr/bin:/home/js/usr/bin

Добавляет

, Вставляет в данном положении. По умолчанию это добавляет в конце.

{ 
    if(IDX < 1) IDX = NF + IDX + 1
    for(i = 1; i <= NF; i++) {
        if(IDX == i) 
            print REP 
        print $i
    }
    if(IDX == NF + 1)
        print REP
}

Запускаются, это с помощью [1 116]

function path_app {
    echo $PATH | awk -F: -f app.awk REP="$1" IDX="$2" | paste -sd:
}

$ echo $PATH
/bin:/usr/bin:/home/js/usr/bin
$ path_app /baz 0
/bin:/usr/bin:/home/js/usr/bin:/baz
$ path_app /baz -1
/bin:/usr/bin:/baz:/home/js/usr/bin
$ path_app /baz 1
/baz:/bin:/usr/bin:/home/js/usr/bin

Удаляет дубликаты

, Этот сохраняет первые вхождения.

{ 
    for(i = 1; i <= NF; i++) {
        if(!used[$i]) {
            print $i
            used[$i] = 1
        }
    }
}

Запускают его как это:

echo $PATH | awk -F: -f rem_dup.awk | paste -sd:

Проверяют, существуют ли все элементы

, следующее распечатает сообщение об ошибке для всех записей, которые не являются существующими в файловой системе и возвращают ненулевое значение.

echo -n $PATH | xargs -d: stat -c %n

, Чтобы просто проверить, являются ли все элементы путями и получают код возврата, можно также использовать test:

echo -n $PATH | xargs -d: -n1 test -d
1
ответ дан Johannes Schaub - litb 28 November 2019 в 00:14
поделиться

Первой вещью появиться в мою голову для изменения просто части строки является sed замена.

пример: если $PATH эха => "/usr/pkg/bin:/usr/bin:/bin:/usr/pkg/games:/usr/pkg/X11R6/bin" затем для изменения "/usr/bin" на "/usr/local/bin" мог бы быть сделан как это:

## производит файл

стандартного вывода ## "=", символ используется вместо наклонной черты (" / "), так как это было бы грязно, # альтернативный символ заключения в кавычки должно быть маловероятным в ПУТИ

## символ разделителя пути ":" и удален и повторно добавлен здесь, # мог бы хотеть дополнительное двоеточие после последнего пути

$PATH эха | sed '=/usr/bin: =/usr/local/bin: ='

Это решение заменяет весь элемент пути, так могло бы быть избыточным, если новый элемент подобен.

, Если новый ПУТЬ '-s не является динамичным, но всегда в некотором постоянном наборе, Вы могли бы сохранить те в переменной и присвоиться по мере необходимости:

$TEMP_PATH_1 PATH=; # управляет...; \n PATH= $TEMP_PATH_2; # управляет и т.д....;

не Мог бы быть тем, что Вы думали. некоторые соответствующие команды на ударе/Unix были бы:

pushd popd CD ls #, возможно, l-1A для отдельного столбца; найдите grep, какой # мог подтвердить, что файл - то, где Вы думаете, что это прибыло из; огибающий тип

.. и все это и больше имеют [приблизительно 111] влияние на ПУТЬ или каталоги в целом. Текст, изменяющий часть, мог быть сделан любое количество путей!

Независимо от того, что выбранное решение имело бы 4 части:

1) выбирают путь, как это, 2) декодируют путь для нахождения изменений необходимости части 3) determing, какие изменения нуждаются/интегрируются те изменения 4) интеграция/установка проверки/финала переменная

0
ответ дан waynecolvin 28 November 2019 в 00:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: