我想知道是否有人有好方法在bash中添加/删除环境变量中的路径(而不仅仅是PATH
变量)。
我的情况是我在集群上实现了一个类似于模块的简单系统,我没有管理员权限。我希望能够编写如下脚本:
#!/bin/bash
if [ $1 == "load" ]; then
path_prepend PATH "$HOME/apps/openmpi-1.8.1-gcc-4.9.0/bin"
path_prepend CPATH "$HOME/apps/openmpi-1.8.1-gcc-4.9.0/include"
...
fi
if [ $1 == "unload" ]; then
path_remove PATH "$HOME/apps/openmpi-1.8.1-gcc-4.9.0/bin"
path_remove CPATH "$HOME/apps/openmpi-1.8.1-gcc-4.9.0/include"
...
fi
我发现questions dealing具体修改PATH
变量,但不是一般环境变量。看起来这些解决方案的一般版本将是一个有用的工具。
我目前的解决方案是在我的.bash_profile
中提供以下内容,但我想知道是否有人有更优雅的解决方案(没有安装实际Modules system的本地副本)。对于使用这么多eval
和echo
s我感到很不舒服,这是我宁愿避免的做法。
#!/bin/bash
# modified from https://stackoverflow.com/questions/370047/
function path_remove () {
eval export $(echo $1=\$\(echo -n $(echo -n "\$$1 | awk -v RS=: -v ORS=: '\$0 != \"'$2'\"' | sed 's/:\$//'")\))
}
function path_append () {
path_remove $1 $2
eval export $1="\$$1:$2"
}
function path_prepend () {
path_remove $1 $2
eval export $1="$2:\$$1"
}
答案 0 :(得分:3)
以下是紧凑的bash
函数,其中:
eval
来间接设置变量,以避免printf -v
,如@Charles Duffy's excellent answer中那样。警告:现有的重复条目只有在不直接相邻时才会被删除(请参阅下面的替代方案)。
#!/usr/bin/env bash
# The functions below operate on PATH-like variables whose fields are separated
# with ':'.
# Note: The *name* of the PATH-style variable must be passed in as the 1st
# argument and that variable's value is modified *directly*.
# SYNOPSIS: path_prepend varName path
# Note: Forces path into the first position, if already present.
# Duplicates are removed too, unless they're directly adjacent.
# EXAMPLE: path_prepend PATH /usr/local/bin
path_prepend() {
local aux=":${!1}:"
aux=${aux//:$2:/:}; aux=${aux#:}; aux=${aux%:}
printf -v "$1" '%s' "${2}${aux:+:}${aux}"
}
# SYNOPSIS: path_append varName path
# Note: Forces path into the last position, if already present.
# Duplicates are removed too, unless they're directly adjacent.
# EXAMPLE: path_append PATH /usr/local/bin
path_append() {
local aux=":${!1}:"
aux=${aux//:$2:/:}; aux=${aux#:}; aux=${aux%:}
printf -v "$1" '%s' "${aux}${aux:+:}${2}"
}
# SYNOPSIS: path_remove varName path
# Note: Duplicates are removed too, unless they're directly adjacent.
# EXAMPLE: path_remove PATH /usr/local/bin
path_remove() {
local aux=":${!1}:"
aux=${aux//:$2:/:}; aux=${aux#:}; aux=${aux%:}
printf -v "$1" '%s' "$aux"
}
如果您需要处理直接相邻的重复项和/或希望能够指定不同的字段分隔符,这里有更精细的功能使用<来自@konsolebox' helpful answer的强>数组技术。
# SYNOPSIS: field_prepend varName fieldVal [sep]
# SEP defaults to ':'
# Note: Forces fieldVal into the first position, if already present.
# Duplicates are removed, too.
# EXAMPLE: field_prepend PATH /usr/local/bin
field_prepend() {
local varName=$1 fieldVal=$2 IFS=${3:-':'} auxArr
read -ra auxArr <<< "${!varName}"
for i in "${!auxArr[@]}"; do
[[ ${auxArr[i]} == "$fieldVal" ]] && unset auxArr[i]
done
auxArr=("$fieldVal" "${auxArr[@]}")
printf -v "$varName" '%s' "${auxArr[*]}"
}
# SYNOPSIS: field_append varName fieldVal [sep]
# SEP defaults to ':'
# Note: Forces fieldVal into the last position, if already present.
# Duplicates are removed, too.
# EXAMPLE: field_append PATH /usr/local/bin
field_append() {
local varName=$1 fieldVal=$2 IFS=${3:-':'} auxArr
read -ra auxArr <<< "${!varName}"
for i in "${!auxArr[@]}"; do
[[ ${auxArr[i]} == "$fieldVal" ]] && unset auxArr[i]
done
auxArr+=("$fieldVal")
printf -v "$varName" '%s' "${auxArr[*]}"
}
# SYNOPSIS: field_remove varName fieldVal [sep]
# SEP defaults to ':'
# Note: Duplicates are removed, too.
# EXAMPLE: field_remove PATH /usr/local/bin
field_remove() {
local varName=$1 fieldVal=$2 IFS=${3:-':'} auxArr
read -ra auxArr <<< "${!varName}"
for i in "${!auxArr[@]}"; do
[[ ${auxArr[i]} == "$fieldVal" ]] && unset auxArr[i]
done
printf -v "$varName" '%s' "${auxArr[*]}"
}
答案 1 :(得分:2)
export
设置一个标志,指定变量应该在环境中。但是,如果它已经存在,则始终将更新传递到环境中;你不需要做任何其他事情。
因此:
PATH=/new/value:$PATH
或
PATH=$PATH:/new/value
...完全足够,除非您想添加自己的逻辑(就像重复数据删除一样)。
如果您只想在不存在重复值的情况下执行操作,则可以编写如下内容:
prepend() {
local var=$1
local val=$2
local sep=${3:-":"}
[[ ${!var} =~ (^|"$sep")"$val"($|"$sep") ]] && return # already present
[[ ${!var} ]] || { printf -v "$var" '%s' "$val" && return; } # empty
printf -v "$var" '%s%s%s' "$val" "$sep" "${!var}" # prepend
}
append() {
local var=$1
local val=$2
local sep=${3:-":"}
[[ ${!var} =~ (^|"$sep")"$val"($|"$sep") ]] && return # already present
[[ ${!var} ]] || { printf -v "$var" '%s' "$val" && return; } # empty
printf -v "$var" '%s%s%s' "${!var}" "$sep" "${val}" # append
}
remove() {
local var=$1
local val=$2
local sep=${3:-":"}
while [[ ${!var} =~ (^|.*"$sep")"$val"($|"$sep".*) ]]; do
if [[ ${BASH_REMATCH[1]} && ${BASH_REMATCH[2]} ]]; then
# match is between both leading and trailing content
printf -v "$var" '%s%s' "${BASH_REMATCH[1]%$sep}" "${BASH_REMATCH[2]}"
elif [[ ${BASH_REMATCH[1]} ]]; then
# match is at the end
printf -v "$var" "${BASH_REMATCH[1]%$sep}"
else
# match is at the beginning
printf -v "$var" "${BASH_REMATCH[2]#$sep}"
fi
done
}
...用作:
prepend PATH /usr/local/bin
remove PATH /usr/local/bin
请注意:
function
关键字,因为没有充分理由打破了POSIX兼容性。 (其他事情我们确实打破了POSIX,但实际上在某种程度上增加了价值)。答案 2 :(得分:0)
最简单的方法,也许最一致的方法是首先将这些字符串拆分为数组:
IFS=: read -ra T <<< "$PATH"
然后向这些数组添加元素:
# Append
T+=("$SOMETHING")
# Prepend or Insert
T=("$SOMETHING" "${T[@]}")
# Remove
for I in "${!T[@]}"; do
if [[ ${T[I]} == "$SOMETHING" ]]; then
unset "T[$I]"
break ## You can skip breaking if you want to remove all matches not just the first one.
fi
done
之后你可以用一个安全的评估值把它放回去:
IFS=: eval 'PATH="${T[*]}"'
实际上如果你有点保守&#34;,你可以先保存IFS
:
OLD_IFS=$IFS; IFS=:
PATH="${T[*]}"
IFS=$OLD_IFS
功能:
shopt -s extglob
function path_append {
local VAR=$1 ELEM=$2 T
IFS=: read -ra T <<< "${!VAR}"
T+=("$ELEM")
[[ $VAR == [[:alpha:]_]*([[:alnum:]_]) ]] && IFS=: eval "$VAR=\${T[*]}"
}
function path_prepend {
local VAR=$1 ELEM=$2 T
IFS=: read -ra T <<< "${!VAR}"
T=("$ELEM" "${T[@]}")
[[ $VAR == [[:alpha:]_]*([[:alnum:]_]) ]] && IFS=: eval "$VAR=\${T[*]}"
}
function path_remove {
local VAR=$1 ELEM=$2 T
IFS=: read -ra T <<< "${!VAR}"
for I in "${!T[@]}"; do
[[ ${T[I]} == "$ELEM" ]] && unset "T[$I]"
done
[[ $VAR == [[:alpha:]_]*([[:alnum:]_]) ]] && IFS=: eval "$VAR=\${T[*]}"
}