Вопрос или проблема
Я пытаюсь скомпилировать wxWidgets, используя MingW, и у меня в пути есть cygwin, что, кажется, вызывает конфликт. Поэтому я хотел бы удалить /d/Programme/cygwin/bin
из переменной PATH и интересно, есть ли какой-то элегантный способ сделать это.
Наивный подход заключался бы в том, чтобы вывести его в файл, удалить вручную и снова задать в системе, но я уверен, что есть способ лучше.
Нет стандартных инструментов для “редактирования” значения $PATH (например, “добавить папку, только если она еще не существует” или “удалить эту папку”). Вы можете выполнить:
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
это будет применено для текущей сессии, если вы хотите изменить это постоянно, добавьте в любой .bashrc, bash.bashrc, /etc/profile – что соответствует вашей системе и требованиям пользователя. Однако, если вы используете BASH, вы можете сделать следующее, если, например, хотите удалить каталог /home/wrong/dir/
из вашей переменной PATH, предполагая, что он в конце:
PATH=$(echo "$PATH" | sed -e 's/:\/home\/wrong\/dir$//')
Таким образом, в вашем случае вы можете использовать
PATH=$(echo "$PATH" | sed -e 's/:\/d\/Programme\/cygwin\/bin$//')
Более простой однострочник.
export PATH=`echo $PATH | tr “:” “\n” | grep -v “anaconda” | tr “\n” “:”`
В bash:
directory_to_remove=/d/Programme/cygwin/bin
PATH=:$PATH:
PATH=${PATH//:$directory_to_remove:/:}
PATH=${PATH#:}; PATH=${PATH%:}
Если вы не используете промежуточную переменную, нужно защитить символы /
в удаляемом каталоге, чтобы они не обрабатывались как конец текста поиска.
PATH=:$PATH:
PATH=${PATH//:\/d\/Programme\/cygwin\/bin:/:}
PATH=${PATH#:}; PATH=${PATH%:}
Первая и третья строка добавляют :
вокруг каждого компонента пути поиска, чтобы избежать особого случая для первого и последнего компонента. Вторая строка удаляет указанный компонент.
Более надежная версия, которая устраняет повторяющиеся записи директора в пути, такие как baz:foo:foo:bar
:
function path_remove {
PATH=":$PATH:"
PATH=${PATH//":"/"::"}
PATH=${PATH//":$1:"/}
PATH=${PATH//"::"/":"}
PATH=${PATH#:}; PATH=${PATH%:}
}
Вторая строка удваивает двоеточия, а четвертая возвращает их к одиночным двоеточиям.
После рассмотрения других представленных здесь вариантов и не полного понимания, как некоторые из них работают, я разработал собственную функцию path_remove
, которую я добавил в свой .bashrc
:
function path_remove {
# Удаляем путь по частям, чтобы никогда случайно не удалить подпути
if [ "$PATH" == "$1" ] ; then PATH="" ; fi
PATH=${PATH//":$1:"/":"} # удаляем любые экземпляры в середине
PATH=${PATH/#"$1:"/} # удаляем любой экземпляр в начале
PATH=${PATH/%":$1"/} # удаляем любой экземпляр в конце
}
и протестировано с
function path_remove_test {
( # Подсессия, чтобы не уничтожить текущий путь оболочки
PATH=$1
path_remove $2
if [ "$PATH" != "$3" ] ; then echo "$1" - "$2" = "$PATH" != "$3" ; fi
)
}
path_remove_test startpath:midpath:endpath startpath midpath:endpath
path_remove_test startpath:midpath:endpath midpath startpath:endpath
path_remove_test startpath:midpath:endpath endpath startpath:midpath
path_remove_test somepath:mypath/mysubpath mypath somepath:mypath/mysubpath
path_remove_test path path ""
В итоге это получилось довольно близко к решению Gilles, но завёрнуто в функцию bash, которая может легко использоваться в командной строке.
Это имеет преимущества, поскольку как функция bash работает как программа без необходимости быть в пути и не требует запуска внешних программ, только манипуляции строками bash.
Это кажется довольно работоспособным, в частности, оно не превращает somepath:mypath/mysubpath
в somepath/mysubpath
, если вы запускаете path_remove mypath
, что было проблемой в моей предыдущей функции path_remove
.
Отличное объяснение того, как работает манипуляция строками bash, можно найти в Advanced Bash-Scripting Guide.
Итак, комбинируя ответы от @gilles и @bruno-a (и пару других трюков с sed), я придумал этот однострочник, который удалит каждый REMOVE_PART из PATH, независимо от того, находится ли он в начале, в середине или в конце PATH
PATH=$(REMOVE_PART="/d/Programme/cygwin/bin" sh -c 'echo ":$PATH:" | sed "s@:$REMOVE_PART:@:@g;s@^:\(.*\):\$@\1@"')
Это выглядит немного сложно, но приятно, что можно сделать это за один раз. ;
используется для соединения двух отдельных команд sed:
s@:$REMOVE_PART:@:@g
(которая заменяет:$REMOVE_PART:
на одно:
)s@^:\(.*\):\$@\1@
(которая удаляет ведущие и завершающие двоеточия, добавленные командой echo)
И на аналогичной основе я только что придумал этот однострочник для добавления ADD_PART в PATH, только если PATH еще не содержит его
PATH=$(ADD_PART="/d/Programme/cygwin/bin" sh -c 'if echo ":$PATH:" | grep -q ":$ADD_PART:"; then echo "$PATH"; else echo "$ADD_PART:$PATH"; fi')
Измените последнюю часть на echo "$PATH:$ADD_PART"
, если вы хотите добавить ADD_PART в конец PATH вместо начала.
…
… или чтобы сделать это еще проще, создайте скрипт с именем remove_path_part
с содержимым
echo ":$PATH:" | sed "s@:$1:@:@g;s@^:\(.*\):\$@\1@"
и скрипт с именем prepend_path_part
с содержимым
if echo ":$PATH:" | grep -q ":$1:"; then echo "$PATH"; else echo "$1:$PATH"; fi
и скрипт с именем append_path_part
с содержимым
if echo ":$PATH:" | grep -q ":$1:"; then echo "$PATH"; else echo "$PATH:$1"; fi
сделайте их все исполнимыми, а затем вызывайте их так:
PATH=$(remove_path_part /d/Programme/cygwin/bin)
PATH=$(prepend_path_part /d/Programme/cygwin/bin)
PATH=$(append_path_part /d/Programme/cygwin/bin)
Аккуратно, даже если я так говорю сам 🙂
Этот метод обрабатывает разделитель пути в начале/конце:
echo $PATH | sed 's/:/\n/g' | grep -v <path description> | xargs | tr ' ' ':'
Объяснение:
echo
переменную $PATH в sed
, который заменяет ‘:’ на новые строки
grep
вывод и удалите строки, соответствующие нашему запросу
xargs
вывод для замены новых строк пробелами
tr
вывод для замены пробелов двоеточиями
xargs
обрабатывает обрезку завершающих значений
цепочите некоторые grep
для удаления кучи путей
Чтобы завершить/дополнить принятое решение Tushar, вы можете:
- избежать необходимости экранирования косых черт в PATH, используя неслешевые разделители
- опустить опцию
-e
, согласно man-странице sed: “Если нет опции -e, –expression, -f, или –file, тогда первым не опциональным аргументом считается sed скрипт для интерпретации.” - использовать флаг
g
(глобально) для удаления всех вхождений
В итоге, это дает что-то вроде этого:
PATH=$(echo "$PATH" | sed 's@:/home/wrong/dir$@@g')
Ниже приведен измененный код из решения Greg Tarsa. Здесь используются только встроенные команды bash. Таким образом, это сэкономит много системных вызовов fork().
# Пример вызова:
# append_to PATH "/usr/local/bin"
function remove_from()
{
local path="${1}"
local dir="${2}"
local -a dirs=()
local old_ifs="${IFS}"
IFS=":"
set -- ${!path}
while [ "$#" -gt "0" ]
do
[ "${1}" != "${dir}" ] && dirs+=("${1}")
shift
done
eval "export ${path}=\"${dirs[*]}\""
IFS="${old_ifs}"
}
function append_to()
{
remove_from "${1}" "${2}"
[ -d "${2}" ] || return
if [ -n "${!1}" ]
then
eval "export ${1}=\"${!1}:${2}\""
else
eval "export ${1}=\"${2}\""
fi
}
function prepend_to()
{
remove_from "${1}" "${2}"
[ -d "${2}" ] || return
if [ -n "${!1}" ]
then
eval "export ${1}=\"${2}:${!1}\""
else
eval "export ${1}=\"${2}\""
fi
}
Возможно использовать глобальные операции Bash и манипуляции строками для удаления пути из списка.
PATH=:$PATH:
while [[ $PATH = *":$dir_to_remove:"* ]]; do
PATH=${PATH//":$dir_to_remove:"/:}
done
# Обрежьте ":" с начала и конца.
PATH=${PATH#:}
PATH=${PATH%:}
Техника с циклом while также устраняет повторяющиеся записи. Например, при удалении foo
из PATH, равного foo:foo:bar
, на первой итерации цикла while он будет :foo:bar:
, а на второй станет :bar:
и цикл завершится, и двоеточия будут обрезаны.
[[
тоже полезная функция Bash, потому что она быстрее, чем [
, и позволяет использовать регулярные выражения и сбои без экранирования.
Использование только встроенных команд Bash значительно ускорит время выполнения.
Интересное упражнение – написать функцию bash для удаления каталога из переменной пути.
Вот несколько функций, которые я использую в своих файлах .bash*, чтобы добавлять/предварительно добавлять каталоги в пути. Их достоинство заключается в удалении дубликатов записей (если таковые имеются) и они работают с любым типом переменной пути через двоеточие (PATH, MANPATH, INFOPATH, …). функция remove_from удаляет каталог.
# {app,pre}pend_to path-var-name dirpath
# remove_from path-var-name dirpath
#
# Функции для манипуляции переменной стиля пути. {app,pre}pend_to
# обе удаляют любые другие экземпляры dirname перед добавлением его в
# начало или конец переменной path-var-name.
#
# Пример вызова:
# append_to PATH "/usr/local/bin"
#
# Использует eval, чтобы передать целевое имя переменной пути.
function remove_from() {
# добавляем окрестные двоеточия
eval tmp_path=":\$${1}:"
# если каталог уже там, удаляем его
(echo "${tmp_path}" | grep --silent ":${2}:") &&
tmp_path=`echo "$tmp_path" | sed "s=:${2}:=:=g"`
# удаляем окрестные двоеточия
tmp_path=`echo "$tmp_path" | sed 's=^:==; s=:$=='`
eval export $1=\"$tmp_path\"
}
function append_to() {
remove_from "$1" "$2" # очищаем содержимое пути
eval export $1=\"\$${1}:$2\"
}
function prepend_to() {
remove_from "$1" "$2" # очищаем содержимое пути
eval export $1=\"${2}:\$$1\"
}
Однострочник в bash с использованием встроенных возможностей:
DIR=/dir/to/remove
PATH=${PATH/${PATH/#$DIR:*/$DIR:}/}${PATH/${PATH/*:$DIR*/:$DIR}/}
- Использует рекурсивное расширение параметров оболочки для удаления двоеточия перед или после удаляемого каталога
- Узор содержит две половины. Одна половина всегда пустая, другая обеспечивает сокращенный путь.
- Удаляет только один экземпляр $DIR из $PATH
В zsh
это будет просто:
path=( ${path:#/d/Programme/cygwin/bin} )
Поскольку там, $path
– это массив, сопоставленный с $PATH
, как в csh/tcsh, и ${array:#pattern}
является оператором для развёртывания элементов массива, за исключением тех, которые соответствуют шаблону.
Смотрите также:
path[(r)/d/Programme/cygwin/bin]=()
Или:
unset 'path[(r)/d/Programme/cygwin/bin]'
Чтобы удалить первый элемент, который соответствует шаблону (здесь фиксированная строка). Замените r
на R
, чтобы удалить последний соответствующий элемент. r
для r
еверсивного индексирования.
Текущие ответы не решают мою аналогичную проблему, так как мне нужно удалить несколько путей. Все эти пути являются подкаталогами одного каталога. В этом случае этот однострочник решает мою проблему: (предположим, шаблон cygwin
, т.е. удалим все пути, содержащие cygwin
)
pattern=cygwin; export PATH=$(echo $PATH|tr ':' '\n'|sed "\#${pattern}#d" |tr '\n' ':')
Я использую bash_path_funcs Стивена Коллиера, описанный в журнале Linux еще в 2000 году:
https://www.linuxjournal.com/article/3645
https://www.linuxjournal.com/article/3768
https://www.linuxjournal.com/article/3935
Функция addpath
добавляет запись в путь только в том случае, если она изначально отсутствует. delpath -n
удаляет все несуществующие каталоги из пути.
Вы можете получить файл pathfunc.tgz
с https://web.archive.org/web/20061210054813/http://www.netspinner.co.uk:80/Downloads/pathfunc.tgz
Я написал простой скрипт на Python, который может решить вашу проблему, я также использую его, когда сталкиваюсь с той же проблемой :
import os
def path_update ( path_r ) :
# получить значение окружения пути
path = os.environ.get("PATH")
# удалить определенный путь
path_update = path.split(":")
path_update = [ i for i in path_update if i != path_r ]
path_update = ":".join(path_update)
# обновление переменной окружения пути
os.environ["PATH"] = path_update
print("Успешное обновление пути ! ! ! ")
if __name__ == "__main__" :
path_to_remove = "/dir1/dir2/dir3/dir4/dir5/app"
path_update( path_to_remove)
Единственное, что вам нужно сделать, это обновить переменную path_to_remove
на удаляемый путь, сохранить и выполнить скрипт.
fzf
и аналогичные инструменты делают TUI более удобным для пользователя, чем GUI.
function path_remove {
local dirs=""
if dirs="$(fzf --multi <<< "${PATH//:/$'\n'}")"; then
local path=":$PATH"
while IFS= read -r line; do
path="${path/":$line"/}"
done <<< "$dirs"
export PATH="${path#:}"
fi
}
export PATH=$(echo $PATH | tr “:” “\n” | grep -v “/wrong/dir/path” | tr “\n” “:” | sed ‘s/:$//g’ | sed -e ‘$a’)
path="/home/test/113/abc/mytest.txt"
while [[ "$path" == */* ]]; do path=$(echo "$path" | cut -d/ -f2-); done
echo "$path"
.
Ответ или решение
Удаление директории из переменной PATH – важная задача для многих разработчиков, стремящихся устранить конфликты сред и библиотек в процессе работы. В данном случае у вас возникла потребность удалить ‘/d/Programme/cygwin/bin’ из вашей PATH-переменной для успешной компиляции wxWidgets с использованием MingW без конфликтов с Cygwin. Решения, предложенные ниже, помогут вам справиться с этой задачей различными способами.
Фундаментальные решения
1. Использование sed для точного изменения PATH:
Использование утилиты sed
позволяет последовательно удалить одну или несколько частей из PATH. Вот как это может быть сделано:
PATH=$(echo "$PATH" | sed 's@:/d/Programme/cygwin/bin:@@g')
Этот метод заменяет все вхождения ‘: /d/Programme/cygwin/bin:’ на ‘:’, эффективно удаляя указанную директорию из PATH. Данную команду можно адаптировать для использования и с другими директориями.
2. Встроенные функции Bash:
Используя исключительно Bash, вы можете воспользоваться следующей встроенной функцией:
directory_to_remove="/d/Programme/cygwin/bin"
PATH=:$PATH:
PATH=${PATH//:${directory_to_remove}:/:}
PATH=${PATH#:}; PATH=${PATH%:}
Этот способ работает быстро благодаря встроенной обработке строк Bash, что исключает необходимость вызова внешних утилит.
Углубленные методы
3. Написание функций для управления PATH переменной:
Создание функции для манипуляции PATH может быть удобным и эффективным:
function path_remove {
if [ "$PATH" == "$1" ] ; then PATH="" ; fi
PATH=${PATH//":$1:"/":"}
PATH=${PATH/#"$1:"/}
PATH=${PATH/%":$1"/}
}
Вы можете добавить эту функцию в ваш .bashrc
, чтобы пользоваться ей постоянно. Это позволит легко и быстро изменять переменную PATH без постоянного редактирования каждой строки вручную.
4. Использование Python для наглядности:
Если вы предпочитаете использовать Python, вот простой скрипт, который поможет вам с этой задачей:
import os
def path_update(path_r):
path = os.environ.get("PATH")
path_update = path.split(":")
path_update = [i for i in path_update if i != path_r]
path_update = ":".join(path_update)
os.environ["PATH"] = path_update
print("Успешное обновление PATH!")
if __name__ == "__main__":
path_to_remove = "/d/Programme/cygwin/bin"
path_update(path_to_remove)
Этот скрипт удалит определенную директорию из переменной PATH, предлагая вам простой и наглядный способ выполнения задачи.
Удаление директории из PATH – ключевой шаг в управлении окружением разработки. Правильно подобрав метод, вы можете значительно упростить настройку системы и избежать ненужных конфликтов.