awk コマンドで重複した $PATH エントリを削除する

awk コマンドで重複した $PATH エントリを削除する

PATH 環境変数からディレクトリの重複コピーを削除できる bash シェル関数を作成しようとしています。

コマンドを使用して 1 行のコマンドでこれを実現できると聞きましたawkが、その方法がわかりません。誰か方法を知っていますか?

答え1

すでに重複したディレクトリがなくPATH、まだ存在しないディレクトリのみを追加したい場合は、シェルだけで簡単に実行できます。

for x in /path/to/add …; do
  case ":$PATH:" in
    *":$x:"*) :;; # already there
    *) PATH="$x:$PATH";;
  esac
done

以下は、 から重複を削除するシェル スニペットです$PATH。エントリを 1 つずつ調べて、まだ表示されていないエントリをコピーします。

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 が提供されるはずです。

PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')"

これは単にコロン ( split(/:/, $ENV{PATH})) で分割し、を使用してgrep { 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 以外のワンライナーを追加する限り:

PATH=$(zsh -fc "typeset -TU P=$PATH p; echo \$P")

( のように単純になることもありますPATH=$(zsh -fc 'typeset -U path; echo $PATH')が、zsh は常に少なくとも 1 つのzshenv設定ファイルを読み取り、 を変更する可能性がありますPATH。)

2 つの優れた zsh 機能を使用します。

  • 配列に結び付けられたスカラー ( typeset -T)
  • 重複する値を自動的に削除する配列 ( typeset -U)。

関連情報