題 如果目錄尚未存在,請將目錄添加到$ PATH


有沒有人寫過一個bash函數來添加一個目錄到$ PATH只有它還沒有?

我通常使用以下內容添加到PATH:

export PATH=/usr/local/mysql/bin:$PATH

如果我在.bash_profile中構造我的PATH,那麼除非我所在的會話是登錄會話,否則它不會被讀取 - 這並非總是如此。如果我在.bashrc中構造我的PATH,那麼它將與每個子shell一起運行。因此,如果我啟動終端窗口然後運行屏幕然後運行shell腳本,我會得到:

$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....

我將嘗試構建一個名為bash的函數 add_to_path() 如果它不在那裡,它只會添加目錄。但是,如果有人已經寫過(或發現)這樣的事情,我就不會花時間去做。


116
2017-09-11 16:19


起源


看到 stackoverflow.com/questions/273909/... 對於一些可以幫助的基礎設施。 - dmckee
unix.stackexchange.com/questions/4965/... - Ciro Santilli 新疆改造中心 六四事件 法轮功
如果你把這個問題定格為“只在不存在的情況下才會添加”,那麼當這一天到來時,當插入的項目在開始時很重要但是它不會在那裡結束時,你會感到非常驚訝。更好的方法是插入元素,然後刪除重複項,因此如果新條目已經存在,它將有效地移動到開頭。 - Don Hatch


答案:


從我的.bashrc:

pathadd() {
    if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
        PATH="${PATH:+"$PATH:"}$1"
    fi
}

請注意,PATH應已標記為已導出,因此不需要重新導出。這會檢查目錄是否存在並且在添加目錄之前是一個目錄,您可能不關心。

此外,這會將新目錄添加到路徑的末尾;在開始時,使用 PATH="$1${PATH:+":$PATH"}" 而不是上述 PATH= 線。


117
2017-09-12 03:08



我在乎。 - Dennis Williamson
@Neil:它確實有效,因為它與之相比 ":$PATH:" 而不僅僅是 "$PATH" - Gordon Davisson
@GordonDavisson:我道歉,我的測試錯了,你是對的。 - Neil
@GordonDavisson大括號中的內容有什麼意義。我似乎無法解開它“${PATH:+"$PATH:"}$ 1“ - boatcoder
@ Mark0978:這就是我為解決bukzor指出的問題所做的工作。 ${variable:+value} 意思是檢查是否 variable 已定義並具有非空值,如果確實給出了評估結果 value。基本上,如果PATH是非空白的,則將其設置為 "$PATH:$1";如果它是空白的,它將它設置為正好 "$1" (注意缺少冒號)。 - Gordon Davisson


擴展戈登戴維森的答案,這支持多個論點

pathappend() {
  for ARG in "$@"
  do
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="${PATH:+"$PATH:"}$ARG"
    fi
  done
}

所以你可以做pathappend path1 path2 path3 ...

對於前置,

pathprepend() {
  for ((i=$#; i>0; i--)); 
  do
    ARG=${!i}
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="$ARG${PATH:+":$PATH"}"
    fi
  done
}

與pathappend相似,你可以做到

pathprepend path1 path2 path3 ...


19
2018-05-15 18:42



這很棒!我做了一個小改動。對於'pathprepend'函數,反向讀取參數很方便,所以你可以說,例如, pathprepend P1 P2 P3 並最終得到 PATH=P1:P2:P3。要獲得此行為,請進行更改 for ARG in "$@" do 至 for ((i=$#; i>0; i--)); do ARG=${!i} - ishmael
謝謝@ishmael,很好的建議,我編輯了答案。我知道你的評論已經超過兩年了,但我從那時起就沒有回來過。我必須弄清楚如何讓堆棧交換電子郵件登陸我的收件箱! - Guillaume Perrault-Archambault


這是來自的東西 我的答案 至 這個問題 結合Doug Harris的功能結構。它使用Bash正則表達式:

add_to_path ()
{
    if [[ "$PATH" =~ (^|:)"${1}"(:|$) ]]
    then
        return 0
    fi
    export PATH=${1}:$PATH
}

12
2017-09-11 19:11



這對我來說只能使用 $1 代替 ${1} - Andrei
@Andrei:是的,在這種情況下,大括號是不必要的。我不知道為什麼要把它們包括在內。 - Dennis Williamson


把它放在對所選答案的評論中,但評論似乎不支持PRE格式,所以在這裡添加答案:

@ gordon-davisson我不是不必要的引用和連接的忠實粉絲。假設您使用的是bash版本> = 3,您可以改為使用bash的內置正則表達式並執行:

pathadd() {
    if [ -d "$1" ] && [[ ! $PATH =~ (^|:)$1(:|$) ]]; then
        PATH+=:$1
    fi
}

這樣可以正確處理目錄或PATH中有空格的情況。關於bash的內置正則表達式引擎是否足夠緩慢存在一些問題,這可能比你的版本所做的字符串連接和插值效率低,但不知何故,它對我來說感覺更美觀。


10
2018-03-02 18:20



評論支持 formatting using the backtick 只是你沒有得到任何體面的段落控制。 - boatcoder
這使得添加結束。通常希望添加到開頭以覆蓋現有位置。 - Dennis Williamson
@DennisWilliamson這是一個公平的觀點,雖然我不建議將其作為默認行為。要弄清楚如何改變前置並不難。 - Christopher Smith
@ChristopherSmith - re: unnecessary quoting 暗示你提前知道 $PATH 不是空的。 "$PATH" 使PATH無效是否正常。同樣如果 $1 包含可能會混淆命令解析器的字符。將正則表達式放在引號中 "(^|:)$1(:|$)" 防止這種情況 - Jesse Chisholm
@JesseChisholm:實際上,我相信克里斯托弗的觀點是規則是不同的 [[ 和 ]]。我更喜歡引用可能需要引用的所有內容,除非它導致它失敗,但我相信他是對的,並且引用確實不是 需要 周圍 $PATH。另一方面,在我看來你是對的 $1。 - Scott


idempotent_path_prepend ()
{
    PATH=${PATH//":$1"/} #delete any instances in the middle or at the end
    PATH=${PATH//"$1:"/} #delete any instances at the beginning
    export PATH="$1:$PATH" #prepend to beginning
}

如果您需要$ HOME / bin在$ PATH的開頭只出現一次而在其他任何地方出現,請不要接受替代品。


6
2017-08-17 13:31



謝謝,這是一個很好的優雅解決方案,但我發現我必須這樣做 PATH=${PATH/"... 而不是 PATH=${PATH//"......為了讓它發揮作用。 - Mark Booth
雙斜杠形式應匹配任意數量的匹配;單斜杠只匹配第一個(在bash手冊頁中搜索“模式替換”)。不知道為什麼它不起作用...... - andybuckley
這種情況在不尋常的情況下失敗了 $1 是唯一的條目(沒有冒號)。該條目加倍。 - Dennis Williamson
如同指出的那樣,它也會過於激進地刪除 PeterS6g。 - Dennis Williamson


這是一個替代解決方案,具有刪除冗餘entires的額外優勢:

function pathadd {
    PATH=:$PATH
    PATH=$1${PATH//:$1/}
}

此函數的單個參數前置於PATH,並且從現有路徑中刪除相同字符串的第一個實例。換句話說,如果路徑中已存在目錄,則會將其提升到前端而不是作為副本添加。

該函數的工作原理是在路徑前加上一個冒號,以確保所有條目在前面都有一個冒號,然後在刪除該條目的情況下將新條目添加到現有路徑。最後一部分是使用bash執行的 ${var//pattern/sub} 符號;看到 bash手冊 更多細節。


6
2018-01-21 12:29



好好想一想;實施有缺陷。考慮如果你已經發生了會發生什麼 /home/robert 在你的 PATH 你呢 pathadd /home/rob。 - Scott


這是我的(我相信它是幾年前由我的舊實驗室的系統管理員Oscar寫的,這一切都歸功於他),它在我的bashrc中存在了很長時間。它還有一個額外的好處,允許您根據需要預先添加或附加新目錄:

pathmunge () {
        if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
           if [ "$2" = "after" ] ; then
              PATH=$PATH:$1
           else
              PATH=$1:$PATH
           fi
        fi
}

用法:

$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /bin/
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /sbin/ after
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin:/sbin/

5
2017-08-17 18:57