Який найелегантніший спосіб видалити шлях зі змінної $ PATH у Bash?


114

Або загалом, як я можу видалити елемент зі списку, розділеного двокрапкою, у змінній середовища 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 з боку Martin Martin .

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 елементами в скриптах оболонки?


Для будь-якої змінної: WORK=`echo -n ${1} | awk -v RS=: -v ORS=: '$0 != "'${3}'"' | sed 's/:$//'`; eval "export ${2}=${WORK}"але ви повинні називати це як func $VAR VAR pattern(на основі @ martin-york та @ andrew-aylett)
vesperto

Відповіді:


51

Хвилина з awk:

# Strip all paths with SDE in them.
#
export PATH=`echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}'`

Редагувати: це відповідь на коментарі нижче:

$ export a="/a/b/c/d/e:/a/b/c/d/g/k/i:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i"
$ echo ${a}
/a/b/c/d/e:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i

## Remove multiple (any directory with a: all of them)
$ echo ${a} | awk -v RS=: -v ORS=: '/a/ {next} {print}'
## Works fine all removed

## Remove multiple including last two: (any directory with g)
$ echo ${a} | awk -v RS=: -v ORS=: '/g/ {next} {print}'
/a/b/c/d/e:/a/b/c/d/f:
## Works fine: Again!

Редагувати у відповідь на проблему безпеки: (це не стосується питання)

export PATH=$(echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}' | sed 's/:*$//')

Це видаляє всі залишки товстих кольорів, видаливши останні записи, що ефективно додасть .ваш шлях.


1
Помилка при спробі видалити останній елемент або кілька елементів: у першому випадку він додає поточний dir (як порожній рядок; потенційний отвір безпеки), у другому випадку він додає `` як елемент шляху.
Фред Фоо

1
@larsmans: Для мене добре працює. Примітка: Empty - це не те саме, що поточний каталог, який є "./"
Martin York

2
Порожній рядок як «член» з PATHзмінних робить , як спеціальне правило, позначає поточний каталог усіх оболонок Unix , так як , по крайней мере V7 Unix в 1979 році він все ще в bash. Перевірте посібник або спробуйте самі.
Фред Фоо

1
@Martin: POSIX не вимагає такої поведінки, але документує та дозволяє це: pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Фред

1
При видаленні останнього елемента з цим виникає ще більш тонка проблема: awk, здається, додає нульовий байт до кінця рядка . Це означає, що якщо ви додасте інший каталог до PATH пізніше, він насправді не буде шуканий.
sschuberth

55

Мій брудний злом:

echo ${PATH} > t1
vi t1
export PATH=$(cat t1)

18
Це ніколи не хороший знак , коли найбільш очевидним рішенням є де -Автоматизація процесу. :-D
Бен Бланк

Так набагато безпечніше, ніж "елегантні" альтернативи.
Jortstek

44

Оскільки великою проблемою із заміною є кінцеві справи, як же зробити так, щоб кінцеві справи не відрізнялися від інших справ? Якщо на шляху вже були колонки на початку та в кінці, ми могли б просто шукати потрібну нитку, обгорнуту двокрапками. Як це є, ми можемо легко додати ці колонки і видалити їх згодом.

# PATH => /bin:/opt/a dir/bin:/sbin
WORK=:$PATH:
# WORK => :/bin:/opt/a dir/bin:/sbin:
REMOVE='/opt/a dir/bin'
WORK=${WORK/:$REMOVE:/:}
# WORK => :/bin:/sbin:
WORK=${WORK%:}
WORK=${WORK#:}
PATH=$WORK
# PATH => /bin:/sbin

Чистий баш :).


2
Я додав би цей підручник для додаткової глазурі: tldp.org/LDP/abs/html/string-manipulation.html
Cyber ​​Oliveira

1
Я використовував це, тому що це виглядало як найпростіше рішення. Це було дуже швидко і просто, і ви можете легко перевірити свою роботу за допомогою echo $ WORK перед останнім рядком, де ви фактично змінюєте змінну PATH.
Філ Гран

2
Зовсім маленький коштовний камінь. Саме те, що я намагався зробити, коли знайшов цю посаду. -Дякую, Андрій! BTW: Можливо, ви хочете додати подвійні лапки навколо ": $ PATH:", на всякий випадок, коли він повинен містити пробіли (те саме про "/ usr / bin") та останній рядок "$ WORK".

2
Дякую, @ PacMan-- :). Я впевнений (щойно спробував), вам не потрібні пробіли для призначення WORKі PATHяк розширення змінної відбувається після розбору рядка на секції для призначення змінних та виконання команд. REMOVEможливо, вам потрібно буде цитувати, або ви можете просто поставити рядок прямо на заміну, якщо вона є постійною.
Ендрю Ейлетт

Я завжди заплутався, коли збереглися рядки, тому я починав завжди дублювати рядки. Ще раз дякую за роз’яснення цього. :)

26

Ось найпростіше рішення, яке я можу розробити:

#!/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, який містить "usr". Ви можете замінити "* usr *" на "/ home / user / bin", щоб видалити саме цей елемент.

оновлення за sschuberth

Незважаючи на те, що я вважаю, що простори в а $PATH- це жахлива ідея, ось таке рішення, яке справляється з цим:

PATH=$(IFS=':';t=($PATH);n=${#t[*]};a=();for ((i=0;i<n;i++)); do p="${t[i]%%*usr*}"; [ "${p}" ] && a[i]="${p}"; done;echo "${a[*]}");

або

IFS=':'
t=($PATH)
n=${#t[*]}
a=()
for ((i=0;i<n;i++)); do
  p="${t[i]%%*usr*}"
  [ "${p}" ] && a[i]="${p}"
done
echo "${a[*]}"

2
Як один вкладиш: PATH = $ (IFS = ':'; t = ($ PATH); скасувати IFS; t = ($ {t [@] %% * usr *}); IFS = ':'; відлуння "$ {t [*]} ");
nicerobot

1
Це рішення не працює із шляхами в PATH, які містять пробіли; це замінює їх колонами.
sschuberth

11

Ось однолінійний текст, який, незважаючи на прийняті та найвищі відповіді, не додає до PATH невидимих ​​символів і може впоратися із шляхами, що містять пробіли:

export PATH=$(p=$(echo $PATH | tr ":" "\n" | grep -v "/cygwin/" | tr "\n" ":"); echo ${p%:})

Особисто мені також здається, що це легко читати / розуміти, і воно включає лише загальні команди замість використання awk.


2
... і якщо ви хочете щось, що може впоратися навіть з новими рядками у назви файлів, ви можете скористатися цим: export PATH=$(p=$(echo $PATH | tr ":" "\0" | grep -v -z "/cygwin/" | tr "\0" ":"); echo ${p%:}) (хоча, певно, ви можете запитати себе, навіщо вам це потрібно, якщо ви це робите :))
ehdr

Це видалить часткові сірники, що, мабуть, не те, що ви хочете; Я б використав grep -v "^/path/to/remove\$"абоgrep -v -x "/path/to/remove"
ShadSterling

Прекрасне рішення, але чи справді ви вважаєте, що trце більш часто awk? ;)
К.-Майкл Айе

1
Абсолютно. Легкі середовища, як-от Git Bash в Windows, швидше поставляються з простим інструментом, як-от trзамість перекладача awk.
sschuberth

1
@ehdr: Вам потрібно замінити echo "..."з printf "%s" "..."для того , щоб працювати над шляхами , як -eі інші подібні. Дивіться stackoverflow.com/a/49418406/102441
Ерік

8

Ось таке рішення, яке:

  • чистий Баш,
  • не посилається на інші процеси (наприклад, "sed" або "awk"),
  • не змінюється IFS,
  • не розщеплює підрозділ,
  • обробляє контури з пробілами та
  • видаляє всі випадки аргументу в PATH.

    deleteFromPath () {
       місцевий пд
       p = ": $ 1:"
       d = ": $ PATH:"
       d = $ {d // $ p /:}
       d = $ {d / #: /}
       PATH = $ {d /%: /}
    }

4
Мені подобається це рішення. Може, зробити назви змінних більш описовими?
Anukool

дуже хороша. Однак не працює для сегментів шляху, що містять зірочку (*). Іноді вони потрапляють туди випадково.
Йорг

6

функція __path_remove () {
local D = ": $ {PATH}:";
["$ {D /: $ 1: /:}"! = "$ D"] && PATH = "$ {D /: $ 1: /:}";
PATH = "$ {PATH / #: /}";
export PATH = "$ {PATH /%: /}";
}

Витягніть його з мого файлу .bashrc. Коли ви граєте з PATH, і він втрачається, awk / sed / grep стає недоступним :-)


1
Це дуже вдалий момент. (Я ніколи не любив виконувати зовнішні утиліти для таких простих речей).

6

Найкращий варіант чистого башу, який я знайшов поки що, це:

function path_remove {
  PATH=${PATH/":$1"/} # delete any instances in the middle or at the end
  PATH=${PATH/"$1:"/} # delete any instances at the beginning
}

Це ґрунтується на не зовсім правильній відповіді на додавання каталогу в $ PATH, якщо його вже немає на Superuser.


Це теж непогано. Я тестував це. Якщо в PATH є дубльований шлях (наприклад, два, які абсолютно однакові), то видаляється лише один із них. Ви також можете перетворити його на removePath () { PATH=${PATH/":$1"/}; PATH=${PATH/"$1:"/}; }

Це рішення не вдається, коли $PATHмістить підпапку цільового (тобто видаляється) шляху. Наприклад: a:abc/def/bin:b-> a/bin:b, коли abc/defпотрібно видалити.
Робін Хсу

5

Я щойно використовував функції в дистрибутиві bash, які існують, мабуть, з 1991 року. Вони все ще знаходяться в пакеті bash-docs на Fedora і використовувалися в /etc/profile, але не більше ...

$ rpm -ql bash-doc |grep pathfunc
/usr/share/doc/bash-4.2.20/examples/functions/pathfuncs
$ cat $(!!)
cat $(rpm -ql bash-doc |grep pathfunc)
#From: "Simon J. Gerraty" <sjg@zen.void.oz.au>
#Message-Id: <199510091130.VAA01188@zen.void.oz.au>
#Subject: Re: a shell idea?
#Date: Mon, 09 Oct 1995 21:30:20 +1000


# NAME:
#       add_path.sh - add dir to path
#
# DESCRIPTION:
#       These functions originated in /etc/profile and ksh.kshrc, but
#       are more useful in a separate file.
#
# SEE ALSO:
#       /etc/profile
#
# AUTHOR:
#       Simon J. Gerraty <sjg@zen.void.oz.au>

#       @(#)Copyright (c) 1991 Simon J. Gerraty
#
#       This file is provided in the hope that it will
#       be of use.  There is absolutely NO WARRANTY.
#       Permission to copy, redistribute or otherwise
#       use this file is hereby granted provided that
#       the above copyright notice and this notice are
#       left intact.

# is $1 missing from $2 (or PATH) ?
no_path() {
        eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
  no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
    sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}

4

Я відповів на це відповідь тут (використовуючи також awk). Але я не впевнений, що це те, що ти шукаєш? Принаймні, мені здається зрозумілим, що він робить, замість того, щоб намагатися вписатись в одну лінію. Однак, для простого вкладиша, який видаляє лише речі, я рекомендую

echo $PATH | tr ':' '\n' | awk '$0 != "/bin"' | paste -sd:

Заміна є

echo $PATH | tr ':' '\n' | 
    awk '$0 != "/bin"; $0 == "/bin" { print "/bar" }' | paste -sd:

або (коротше, але менш читабельне)

echo $PATH | tr ':' '\n' | awk '$0 == "/bin" { print "/bar"; next } 1' | paste -sd:

У всякому разі, про те саме запитання та цілу масу корисних відповідей дивіться тут .


І якщо ви хочете видалити рядки, які містять часткове використання рядків awk '$0 !~ "/bin"'. Тобто зберігайте рядки, які не містять '/ bin', з оператором awk !~.
thoni56

3

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

PATH=${PATH/:\/home\/user\/bin/}

Це не лише розширення імені шляху, а не регулярні вирази?
dreamlax

2
Хоча bash підтримує регулярні вирази (станом на bash 3), це не є прикладом цього, це підміна змінної.
Роберт Гембл

4
Це розширення шаблону змінних, і рішення має кілька проблем. 1) він не буде відповідати першому елементу. 2) воно буде відповідати всьому, що починається з "/ home / user / bin", а не лише "/ home / user / bin". 3) вона вимагає втечі спеціальних символів. У кращому випадку я б сказав, що це неповний приклад.
nicerobot

2

Мені подобаються три функції, показані в оновленнях @ BenBlank до його початкового запитання. Для їх узагальнення я використовую 2-аргументальну форму, яка дозволяє мені встановити 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"

Зауважте, що я також додав кілька лапок, щоб дозволити належну обробку імен шляхів, що містять пробіли.


2

Який найелегантніший спосіб видалити шлях зі змінної $ PATH у Bash?

Що є більш елегантним, ніж awk?

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

Пітон! Це більш читабельне та зручне рішення, і його легко перевірити, побачивши, що він справді робить те, що ви хочете.

Скажіть, ви хочете видалити перший елемент шляху?

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

Так, наприклад, розміщення двокрапки в кінці 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

Якщо ви стурбовані видаленням дублікатів у $ PATH, найелегантнішим способом, IMHO, було б не додавати їх в першу чергу. У 1 рядку:

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

$ папку можна замінити чим завгодно, і вона може містити пробіли ("/ home / user / my Documents")


1

Найелегантніше чисте баш-рішення, яке я знайшов на сьогодні:

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"                                  
} 

1

Більшість інших запропонованих рішень покладатися тільки на збігу рядків і не враховує сегменти шляху , що містять спеціальні імена , як ., ..або ~. Функція 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'

плюс один за рамки
Martin York

1

Linux від Scratch визначає три функції 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

Я знаю, що це питання задає питання про BASH, якому всі бажають віддавати перевагу, але оскільки мені подобається симетрія, і іноді мені потрібно використовувати "csh", я створив еквівалент "path_prepend ()", "path_append ()" та "path_remove () "елегантне рішення вище.

Суть у тому, що "csh" не має функцій, тому я поміщаю невеликі скрипти оболонки в свій особистий каталог бін, які діють як функції. Я створюю псевдоніми Джерело цих сценаріїв, щоб змінити змінену зміну середовища.

~ / 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

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

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

Поширена проблема в створенні програмного забезпечення полягає в тому, що він працює на деяких машинах, в кінцевому рахунку через те, як хтось налаштував свою оболонку за замовчуванням (PATH та інші змінні середовища). Елегантне рішення - зробити так, щоб ваші сценарії побудови були захищені, повністю вказавши середовище оболонки. Зашифруйте свої сценарії побудови, щоб встановити PATH та інші змінні середовища на основі складання елементів, якими ви керуєте, таких як розташування компілятора, бібліотеки, інструменти, компоненти тощо. Зробіть для кожного конфігуруваного елемента щось, що ви можете індивідуально встановити, перевірити та потім використовувати належним чином у своєму сценарії.

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

До речі, не використовуйте Maven, Java в порядку, і купуйте WebLogic лише у тому випадку, якщо вам абсолютно потрібна його кластеризація (але в іншому випадку немає, і особливо не її власні функції).

Найкращі побажання.


іноді у вас немає кореневого доступу, і ваш адміністратор керує вашим PATH. Звичайно, ви можете створити свою власну, але кожного разу, коли ваш адміністратор щось пересуває, ви повинні з'ясувати, куди він це поставив. Цей вид перемагає призначення адміністратора.
Шеп

0

Як і у @litb, я написав відповідь на запитання " Як я маніпулюю елементами $ PATH у скриптах оболонки ", тому моя головна відповідь є.

Функціональність "розділення" в bashінших похідних оболонок Борна найкраще досягається за $IFSдопомогою роздільника між полями. Наприклад, щоб встановити позиційні аргументи ( $1, $2, ...) до елементів PATH, використання:

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

Він буде працювати нормально, доки в $ PATH немає пробілів. Змусити його працювати для елементів контуру, що містять пробіли, нетривіальна вправа - залишене для зацікавленого читача. З цим, мабуть, простіше впоратися з використанням сценаріїв, таких як Perl.

У мене також є сценарій, clnpathякий я широко використовую для встановлення своєї PATH. Я задокументував це у відповіді на тему " Як уберегтися від дублювання змінної PATH в csh ".


IFS =: a = ($ PATH); IFS = розщеплення також приємно. працює, якщо вони містять пробіли. але тоді ви отримали масив, і вам доведеться возитись за циклами та такими, щоб видалити імена.
Йоханнес Шауб - ліб

Так; це стає химерно - як і в моєму оновленому коментарі, напевно, простіше використовувати мову сценаріїв на даний момент.
Джонатан Леффлер

0

Ця проблема дратує це випадки забору серед перших та останніх елементів. Проблему можна елегантно вирішити, змінивши IFS та використовуючи масив, але я не знаю, як знову ввести двокрапку, як тільки шлях перетвориться у форму масиву.

Ось трохи менш елегантна версія, яка вилучає один каталог лише з $PATHвикористанням рядкових маніпуляцій. Я це протестував.

#!/bin/bash
#
#   remove_from_path dirname
#
#   removes $1 from user's $PATH

if [ $# -ne 1 ]; then
  echo "Usage: $0 pathname" 1>&2; exit 1;
fi

delendum="$1"
NEWPATH=
xxx="$IFS"
IFS=":"
for i in $PATH ; do
  IFS="$xxx"
  case "$i" in
    "$delendum") ;; # do nothing
    *) [ -z "$NEWPATH" ] && NEWPATH="$i" || NEWPATH="$NEWPATH:$i" ;;
  esac
done

PATH="$NEWPATH"
echo "$PATH"

0

Ось одноколісний Perl:

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

$aМінлива отримує шлях , який буде видалений. Команди s(заміна) та printкоманди неявно діють на $_змінну.


0

Хороші речі тут. Я використовую цей варіант, щоб у першу чергу не додавати дупи.

#!/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

1
Ви можете спростити свою заяву, додавши до рядка PATH провідні та зворотні двокрапки:case ":$PATH:" in (*:"$nodup":*) ;; (*) PATH="$PATH:$nodup" ;; esac
glenn jackman

0

Якщо ввімкнено розширений глобус, можна зробити наступне:

# 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

Розширений глобальний однолінійний (добре, вид):

path_remove ()  { shopt -s extglob; PATH="${PATH//+(${1})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; } 

Здається, не потрібно уникати косоокості в $ 1.

path_remove ()  { shopt -s extglob; declare escArg="${1//\//\\/}"; PATH="${PATH//+(${escArg})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; } 

0

Додаючи колони до 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 

0

У path_remove_all (через проксі):

-newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}" 
+newPATH="${newPATH//:${@:${i}:1}*([^:])/}"        # s/:\/path[^:]*//g 

0

Хоча це дуже стара тема, я подумав, що це рішення може зацікавити:

PATH="/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
REMOVE="ccache" # whole or part of a path :)
export PATH=$(IFS=':';p=($PATH);unset IFS;p=(${p[@]%%$REMOVE});IFS=':';echo "${p[*]}";unset IFS)
echo $PATH # outputs /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

знайшов це в цій публікації в блозі . Я думаю, що цей мені найбільше подобається :)


0

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

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 у моїх dotfiles . Вони повністю підтримують видалення / додавання / попередження / вставлення на початку / середині / в кінці рядка PATH.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.