我正在嘗試編寫一個 bash shell 函數,該函數允許我從 PATH 環境變數中刪除目錄的重複副本。
有人告訴我,可以使用該命令透過一行命令來實現這一目標awk
,但我不知道如何做到這一點。有人知道怎麼做嗎?
答案1
如果您在 中還沒有重複項,PATH
並且只想新增尚不存在的目錄,則可以僅使用 shell 輕鬆完成此操作。
for x in /path/to/add …; do
case ":$PATH:" in
*":$x:"*) :;; # already there
*) PATH="$x:$PATH";;
esac
done
這是一個從 中刪除重複項的 shell 片段$PATH
。它會逐一瀏覽條目,並複製那些尚未看過的條目。
if [ -n "$PATH" ]; then
old_PATH=$PATH:; PATH=
while [ -n "$old_PATH" ]; do
x=${old_PATH%%:*} # the first remaining entry
case $PATH: in
*:"$x":*) ;; # already there
*) PATH=$PATH:$x;; # not there yet
esac
old_PATH=${old_PATH#*:}
done
PATH=${PATH#:}
unset old_PATH x
fi
答案2
這是一個易懂的單行解決方案可以完成所有正確的事情:刪除重複項,保留路徑的順序,並且不在末尾添加冒號。因此,它應該為您提供一個經過重複資料刪除的 PATH,其行為與原始路徑完全相同:
PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')"
它只是在冒號 ( split(/:/, $ENV{PATH})
) 上分割,使用 usegrep { not $seen{$_}++ }
過濾掉除第一次出現之外的所有重複路徑實例,然後將剩餘的路徑重新連接在一起,並用冒號分隔並列印結果 ( print join(":", ...)
)。
如果您想要更多的結構,以及刪除其他變數重複的能力,請嘗試這個片段,我目前在我自己的配置中使用它:
# Deduplicate path variables
get_var () {
eval 'printf "%s\n" "${'"$1"'}"'
}
set_var () {
eval "$1=\"\$2\""
}
dedup_pathvar () {
pathvar_name="$1"
pathvar_value="$(get_var "$pathvar_name")"
deduped_path="$(perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, $ARGV[0]))' "$pathvar_value")"
set_var "$pathvar_name" "$deduped_path"
}
dedup_pathvar PATH
dedup_pathvar MANPATH
程式碼將對 PATH 和 MANPATH 進行重複刪除,並且您可以輕鬆呼叫其他儲存dedup_pathvar
以冒號分隔的路徑清單的變數(例如 PYTHONPATH)。
答案3
這是一個時尚的:
printf %s "$PATH" | awk -v RS=: -v ORS=: '!arr[$0]++'
更長(看看它是如何工作的):
printf %s "$PATH" | awk -v RS=: -v ORS=: '{ if (!arr[$0]++) { print $0 } }'
好的,由於您是 Linux 新手,因此以下是如何實際設定不含尾隨「:」的 PATH
PATH=`printf %s "$PATH" | awk -v RS=: '{ if (!arr[$0]++) {printf("%s%s",!ln++?"":":",$0)}}'`
順便說一句,請確保您的 PATH 中沒有包含“:”的目錄,否則會弄亂。
一些功勞:
答案4
只要我們加入非 awk oneliners:
PATH=$(zsh -fc "typeset -TU P=$PATH p; echo \$P")
(可能很簡單,PATH=$(zsh -fc 'typeset -U path; echo $PATH')
但 zsh 總是讀取至少一個zshenv
配置文件,該文件可以修改PATH
。)
它使用了兩個很好的 zsh 功能:
- 與陣列關聯的標量 (
typeset -T
) - 以及自動刪除重複值的陣列 (
typeset -U
)。