Какой самый элегантный способ удалить путь из переменной $ PATH в Bash? - PullRequest
99 голосов
/ 16 декабря 2008

Или, в более общем смысле, как удалить элемент из списка, разделенного двоеточиями, в переменной среды Bash?

Мне показалось, что я видел простой способ сделать это несколько лет назад, используя более продвинутые формы расширения переменных Bash, но если это так, я потерял след. Быстрый поиск в Google показал удивительно мало релевантных результатов и ни одного, который я бы назвал «простым» или «элегантным». Например, два метода с использованием sed и awk соответственно:

PATH=$(echo $PATH | sed -e 's;:\?/home/user/bin;;' -e 's;/home/user/bin:\?;;')
PATH=!(awk -F: '{for(i=1;i<=NF;i++){if(!($i in a)){a[$i];printf s$i;s=":"}}}'<<<$PATH)

Ничего простого не существует? Есть ли что-нибудь аналогичное функции split () в Bash?

Обновление:
Похоже, мне нужно извиниться за мой намеренно неопределенный вопрос; Меня меньше интересовало решение конкретного варианта использования, чем провоцирование хорошей дискуссии. К счастью, я понял!

Здесь есть несколько очень умных техник. В конце я добавил следующие три функции в свою панель инструментов. Волшебство происходит в path_remove, в основе которого лежит умное использование Мартином Йорком переменной awk RS.

path_append ()  { path_remove $1; export PATH="$PATH:$1"; }
path_prepend () { path_remove $1; export PATH="$1:$PATH"; }
path_remove ()  { export PATH=`echo -n $PATH | awk -v RS=: -v ORS=: '$0 != "'$1'"' | sed 's/:$//'`; }

Единственный реальный промысел там - это использование sed для удаления заднего двоеточия. Учитывая то, насколько простым является решение Мартина, я вполне готов с этим смириться!


Смежный вопрос: Как мне манипулировать элементами $ PATH в скриптах оболочки?

Ответы [ 34 ]

0 голосов
/ 22 октября 2014

Это, конечно, элегантно, но в нем используется внешний седан. Кроме того, он удаляет все пути, содержащие строку поиска $ 1. И при этом это не оставляет зависание: в конце в случае, если удаленный путь является последним в ПУТИ.

PATH=`echo $PATH | sed 's/:[^:]*$1[^:]*//g'`

Эта альтернатива оставляет финал висячей: однако.

PATH=`echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":"`

Альтернативы без обратных кавычек:

PATH=$(echo $PATH | sed 's/:[^:]*$1[^:]*//g')

PATH=$(echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":")
0 голосов
/ 22 октября 2014

Это, конечно, элегантно, но в нем используется внешний седан. Кроме того, он удаляет все пути, содержащие строку поиска $1. И при этом это не оставляет висячего: в конце, если удаленный путь является последним в ПУТИ.

PATH=`echo $PATH | sed 's/:[^:]*$1[^:]*//g'`

Эта альтернатива оставляет окончательный финал: однако.

PATH=`echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":"`

PS: я не знаю, как сделать так, чтобы мои галочки отображались в коде. Итак, альтернативы:

PATH=$(echo $PATH | sed 's/:[^:]*$1[^:]*//g')

PATH=$(echo $PATH | tr ":" "\n" | grep -v $1 | tr "\n" ":")
0 голосов
/ 16 мая 2014

Трейлинг ':' вызван тем, что вы устанавливаете конец строки, а не разделитель. Я использую ограниченные в ресурсах единицы и люблю собирать все в один скрипт без этих странностей:

path_remove () {
    PATH="$(echo -n $PATH | awk -v RS=: -v ORS= '$0 != "'$1'"{print s _ $0;s=":"}')"
}
0 голосов
/ 16 декабря 2008

Как и в случае @litb, я добавил ответ на вопрос " Как мне манипулировать элементами $ PATH в сценариях оболочки ", поэтому мой основной ответ здесь.

Функциональность 'split' в bash и других производных оболочки Bourne наиболее аккуратно достигается с помощью $IFS, межполевого разделителя. Например, чтобы установить позиционные аргументы ($1, $2, ...) для элементов PATH, используйте:

set -- $(IFS=":"; echo "$PATH")

Это будет работать нормально, если в $ PATH нет пробелов. Заставить его работать с элементами пути, содержащими пробелы, - нетривиальное упражнение, оставленное для заинтересованного читателя. Вероятно, проще справиться с этим, используя язык сценариев, такой как Perl.

У меня также есть скрипт, clnpath, который я широко использую для настройки моего PATH. Я задокументировал это в ответе на « Как избежать дублирования переменной PATH в csh ».

0 голосов
/ 13 января 2010

Хорошие вещи здесь. Я использую этот, чтобы не добавлять дуплексов.

#!/bin/bash
#
######################################################################################
#
# Allows a list of additions to PATH with no dupes
# 
# Patch code below into your $HOME/.bashrc file or where it
# will be seen at login.
#
# Can also be made executable and run as-is.
#
######################################################################################

# add2path=($HOME/bin .)                  ## uncomment space separated list 
if [ $add2path ]; then                    ## skip if list empty or commented out
for nodup in ${add2path[*]}
do
    case $PATH in                 ## case block thanks to MIKE511
    $nodup:* | *:$nodup:* | *:$nodup ) ;;    ## if found, do nothing
    *) PATH=$PATH:$nodup          ## else, add it to end of PATH or
    esac                          ## *) PATH=$nodup:$PATH   prepend to front
done
export PATH
fi
## debug add2path
echo
echo " PATH == $PATH"
echo
0 голосов
/ 06 февраля 2012

Я придерживался немного другого подхода, чем большинство людей здесь, и сосредоточился именно на манипуляции со строками, например:

path_remove () {
    if [[ ":$PATH:" == *":$1:"* ]]; then
        local dirs=":$PATH:"
        dirs=${dirs/:$1:/:}
        export PATH="$(__path_clean $dirs)"
    fi
}
__path_clean () {
    local dirs=${1%?}
    echo ${dirs#?}
}

Выше приведен упрощенный пример последних функций, которые я использую. Я также создал path_add_before и path_add_after, позволяющие вставлять путь до / после указанного пути уже в PATH.

Полный набор функций доступен в path_helpers.sh в моих точечных файлах . Они полностью поддерживают удаление / добавление / добавление / вставку в начале / середине / конце строки PATH.

0 голосов
/ 20 января 2010

При включенном расширенном перемещении можно сделать следующее:

# delete all /opt/local paths in PATH
shopt -s extglob 
printf "%s\n" "${PATH}" | tr ':' '\n' | nl
printf "%s\n" "${PATH//+(\/opt\/local\/)+([^:])?(:)/}" | tr ':' '\n' | nl 

man bash | less -p extglob
0 голосов
/ 08 июня 2018

Я знаю, что этот вопрос задает вопрос о BASH, который каждый должен предпочесть, но, поскольку я наслаждаюсь симметрией, а иногда мне необходимо использовать «csh», я создал эквивалент «path_prepend ()», «path_append ()» и "path_remove ()" элегантное решение выше.

Суть в том, что "csh" не имеет функций, поэтому я поместил небольшие сценарии оболочки в свой личный каталог bin, которые действуют как функции. Я создаю псевдонимы для ИСТОЧНИКА этих сценариев, чтобы внести изменения в назначенную переменную среды.

~ / bin / _path_remove.csh:

set _resolve = `eval echo $2`
setenv $1 `eval echo -n \$$1 | awk -v RS=: -v ORS=: '$1 != "'${_resolve}'"' | sed 's/:$//'`;
unset _resolve

~ / bin / _path_append.csh:

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_base}:${_resolve}
unset _base _resolve

~ / bin / _path_prepend.csh:

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_resolve}:${_base}
unset _base _resolve

~ / bin / .cshrc:

…
alias path_remove  "source ~/bin/_path_remove.csh  '\!:1' '\!:2'"
alias path_append  "source ~/bin/_path_append.csh  '\!:1' '\!:2'"
alias path_prepend "source ~/bin/_path_prepend.csh '\!:1' '\!:2'"
…

Вы можете использовать их вот так ...

%(csh)> path_append MODULEPATH ${HOME}/modulefiles
0 голосов
/ 22 января 2010

В path_remove_all (по доверенности):

-newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}" 
+newPATH="${newPATH//:${@:${i}:1}*([^:])/}"        # s/:\/path[^:]*//g 
0 голосов
/ 22 января 2010

Добавляя двоеточия в PATH, мы могли бы сделать что-то вроде:

path_remove ()  { 
   declare i newPATH
   # put a colon at the beginning & end AND double each colon in-between
   newPATH=":${PATH//:/::}:"   
   for ((i=1; i<=${#@}; i++)); do
       #echo ${@:${i}:1}
       newPATH="${newPATH//:${@:${i}:1}:/}"   # s/:\/fullpath://g
   done
   newPATH="${newPATH//::/:}"
   newPATH="${newPATH#:}"      # remove leading colon
   newPATH="${newPATH%:}"      # remove trailing colon
   unset PATH 
   PATH="${newPATH}" 
   export PATH
   return 0 
} 


path_remove_all ()  {
   declare i newPATH extglobVar
   extglobVar=0
   # enable extended globbing if necessary
   [[ ! $(shopt -q extglob) ]]  && { shopt -s extglob; extglobVar=1; }
   newPATH=":${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}"     # s/:\/path[^:]*//g
   done
   newPATH="${newPATH#:}"      # remove leading colon
   newPATH="${newPATH%:}"      # remove trailing colon
   # disable extended globbing if it was enabled in this function
   [[ $extglobVar -eq 1 ]] && shopt -u extglob
   unset PATH 
   PATH="${newPATH}" 
   export PATH
   return 0 
} 

path_remove /opt/local/bin /usr/local/bin

path_remove_all /opt/local /usr/local 
...