Какой самый элегантный способ удалить путь из переменной $ 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 ]

3 голосов
/ 16 декабря 2008

Ну, в bash, поскольку он поддерживает регулярные выражения, я бы просто сделал:

PATH=${PATH/:\/home\/user\/bin/}
1 голос
/ 10 июля 2016

Какой самый элегантный способ удалить путь из переменной $ PATH в Bash?

Что элегантнее, чем awk?

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

Python! Это более удобочитаемое и удобное в обслуживании решение, и его легко проверить, действительно ли оно делает то, что вам нужно.

Скажем, вы хотите удалить первый элемент пути?

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"

(Вместо конвейера от echo, os.getenv['PATH'] будет немного короче и даст тот же результат, что и выше, но я беспокоюсь, что Python может что-то сделать с этой переменной среды, поэтому, вероятно, лучше передавайте это напрямую из среды, которая вам небезразлична.)

Аналогично удалить с конца:

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"

Чтобы сделать эти многоразовые функции оболочки, которые вы можете, например, вставить в ваш файл .bashrc:

strip_path_first () {
    PATH="$(echo "$PATH" | 
    python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"
}

strip_path_last () {
    PATH="$(echo "$PATH" | 
    python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"
}
1 голос
/ 22 ноября 2015

Linux с нуля определяет три функции Bash в /etc/profile:

# Functions to help us manage paths.  Second argument is the name of the
# path variable to be modified (default: PATH)
pathremove () {
        local IFS=':'
        local NEWPATH
        local DIR
        local PATHVARIABLE=${2:-PATH}
        for DIR in ${!PATHVARIABLE} ; do
                if [ "$DIR" != "$1" ] ; then
                  NEWPATH=${NEWPATH:+$NEWPATH:}$DIR
                fi
        done
        export $PATHVARIABLE="$NEWPATH"
}

pathprepend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="$1${!PATHVARIABLE:+:${!PATHVARIABLE}}"
}

pathappend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="${!PATHVARIABLE:+${!PATHVARIABLE}:}$1"
}

export -f pathremove pathprepend pathappend

Ссылка: http://www.linuxfromscratch.org/blfs/view/svn/postlfs/profile.html

1 голос
/ 19 февраля 2011

Если вас беспокоит удаление дубликатов в $ PATH, самый элегантный способ, IMHO, это не добавлять их в первую очередь. В 1 строке:

if ! $( echo "$PATH" | tr ":" "\n" | grep -qx "$folder" ) ; then PATH=$PATH:$folder ; fi
Папка

$ может быть заменена на что угодно и может содержать пробелы ("/ home / user / my documents")

1 голос
/ 23 сентября 2014

Большинство других предлагаемых решений основаны только на сопоставлении строк и не учитывают сегменты пути, содержащие специальные имена, такие как ., .. или ~. Приведенная ниже функция bash разрешает строки каталога в своем аргументе и сегментах пути, чтобы найти логические совпадения каталогов, а также совпадения строк.

rm_from_path() {
  pattern="${1}"
  dir=''
  [ -d "${pattern}" ] && dir="$(cd ${pattern} && pwd)"  # resolve to absolute path

  new_path=''
  IFS0=${IFS}
  IFS=':'
  for segment in ${PATH}; do
    if [[ ${segment} == ${pattern} ]]; then             # string match
      continue
    elif [[ -n ${dir} && -d ${segment} ]]; then
      segment="$(cd ${segment} && pwd)"                 # resolve to absolute path
      if [[ ${segment} == ${dir} ]]; then               # logical directory match
        continue
      fi
    fi
    new_path="${new_path}${IFS}${segment}"
  done
  new_path="${new_path/#${IFS}/}"                       # remove leading colon, if any
  IFS=${IFS0}

  export PATH=${new_path}
}

Тест:

$ mkdir -p ~/foo/bar/baz ~/foo/bar/bif ~/foo/boo/bang
$ PATH0=${PATH}
$ PATH=~/foo/bar/baz/.././../boo/././../bar:${PATH}  # add dir with special names
$ rm_from_path ~/foo/boo/../bar/.  # remove same dir with different special names
$ [ ${PATH} == ${PATH0} ] && echo 'PASS' || echo 'FAIL'
1 голос
/ 05 января 2016

Мне нравятся три функции, показанные в обновлении @ BenBlank к его первоначальному вопросу. Чтобы обобщить их, я использую форму с двумя аргументами, которая позволяет мне установить PATH или любую другую переменную среды, которую я хочу:

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

Примеры использования:

path_prepend PATH /usr/local/bin
path_append PERL5LIB "$DEVELOPMENT_HOME/p5/src/perlmods"

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

1 голос
/ 21 января 2010

Да, например, размещение двоеточия в конце PATH делает удаление пути менее неуклюжим и подверженным ошибкам.

path_remove ()  { 
   declare i newPATH
   newPATH="${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      #echo ${@:${i}:1}
      newPATH="${newPATH//${@:${i}:1}:/}" 
   done
   export PATH="${newPATH%:}" 
   return 0; 
} 

path_remove_all ()  {
   declare i newPATH
   shopt -s extglob
   newPATH="${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      newPATH="${newPATH//+(${@:${i}:1})*([^:]):/}" 
      #newPATH="${newPATH//+(${@:${i}:1})*([^:])+(:)/}" 
   done
   shopt -u extglob 
   export PATH="${newPATH%:}" 
   return 0 
} 

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

path_remove_all /opt/local /usr/local 
1 голос
/ 07 апреля 2012

Самое элегантное решение для bash, которое я нашел на сегодняшний день:

pathrm () {                                                                      
  local IFS=':'                                                                  
  local newpath                                                                  
  local dir                                                                      
  local pathvar=${2:-PATH}                                                       
  for dir in ${!pathvar} ; do                                                    
    if [ "$dir" != "$1" ] ; then                                                 
      newpath=${newpath:+$newpath:}$dir                                          
    fi                                                                           
  done                                                                           
  export $pathvar="$newpath"                                                        
}

pathprepend () {                                                                 
  pathrm $1 $2                                                                   
  local pathvar=${2:-PATH}                                                       
  export $pathvar="$1${!pathvar:+:${!pathvar}}"                                  
}

pathappend () {                                                                    
  pathrm $1 $2                                                                   
  local pathvar=${2:-PATH}                                                       
  export $pathvar="${!pathvar:+${!pathvar}:}$1"                                  
} 
0 голосов
/ 16 декабря 2008

Вот одна строка Perl:

PATH=`perl -e '$a=shift;$_=$ENV{PATH};s#:$a(:)|^$a:|:$a$#$1#;print' /home/usr/bin`

Переменная $a возвращает путь для удаления. Команды s (замена) и print неявно работают с переменной $_.

0 голосов
/ 16 декабря 2008

Поскольку это, как правило, довольно проблематично, так как здесь НЕТ элегантного способа, я рекомендую избегать проблемы, изменив решение: создайте свой PATH, а не пытайтесь его разрушить.

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

Общая проблема со сборками программного обеспечения заключается в том, что он ломается на некоторых машинах, в конечном счете из-за того, как кто-то настроил свою оболочку по умолчанию (PATH и другие переменные среды). Элегантное решение состоит в том, чтобы сделать ваши сценарии сборки защищенными, полностью указав среду оболочки. Кодируйте ваши сценарии сборки, чтобы установить переменные PATH и другие переменные среды, основываясь на сборочных элементах, которыми вы управляете, таких как расположение компилятора, библиотек, инструментов, компонентов и т. Д. Сделайте каждый настраиваемый элемент тем, что вы можете индивидуально установить, проверить и затем используйте правильно в вашем скрипте.

Например, у меня есть основанная на Maven сборка Java с WebLogic, которую я унаследовал у моего нового работодателя. Сценарий сборки печально известен своей хрупкостью, и еще один новый сотрудник и я потратили три недели (не полный рабочий день, просто здесь и там, но еще много часов), чтобы заставить его работать на наших машинах. Важным шагом было то, что я взял под контроль PATH, чтобы точно знать, какая Java, какая Maven и какая WebLogic вызывались. Я создал переменные окружения, чтобы они указывали на каждый из этих инструментов, затем я рассчитал PATH, основываясь на этих плюс нескольких других. Подобные методы укротили другие настраиваемые параметры, пока мы наконец не создали воспроизводимую сборку.

Кстати, не используйте Maven, с Java все в порядке, и покупайте WebLogic только в том случае, если вам абсолютно необходима его кластеризация (но в противном случае нет, и особенно это не касается его проприетарных функций).

С наилучшими пожеланиями.

...