Pangkas entri duplikat dari variabel PATH

9

Saya sering memodifikasi .bashrc saya dan kemudian sumber. Namun, ketika saya memiliki hal-hal seperti export PATH="~/bin:~/perl5/bin:$PATH"di file saya, maka PATHvariabel lingkungan tumbuh setiap kali saya sumber file.

Misalnya, pertama kali .bashrc bersumber, PATHvariabel terdiri dari ~/bin:~/perl5/bin:/usr/bin:/bin.

Kali kedua terdiri dari ~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Ketiga kalinya terdiri dari ~/bin:~/perl5/bin:~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Apakah ada cara sederhana untuk membuatnya hanya menambahkan sesuatu yang belum ada di PATH?

Christopher Bottoms
sumber

Jawaban:

12

Gunakan fungsi pathmunge () yang tersedia di sebagian besar distro /etc/profile:

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

sunting : Untuk zshpengguna, typeset -U <variable_name>akan menduplikasi entri path.

Sam Halicke
sumber
Terima kasih! Ini bukan /etc/profilepada Debian Lenny, jadi saya memasukkannya ke dalam .bashrc.
Christopher Bottoms
Penggunaan: pathmunge /some/pathakan ditempatkan /some/pathpada awal $PATHdan pathmunge /some/path afterakan ditempatkan /some/pathpada akhir$PATH
Christopher Bottoms
Cleaner cara melakukan cek untuk melihat apakah direktori menebang ada di jalan saat ini, di kerang pesta yang modern: if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then.
Christopher Cashell
Saya tidak akan menggunakan ini. Jika versi naif saya mengatakan PATH = / foo: $ PATH itu berarti saya ingin / foo menang. pathmunge /foo beforetidak mencapai itu. Cara yang lebih baik adalah menambahkan / foo di awal dan kemudian menghapus duplikat sesudahnya.
Don Hatch
3

Saya mengalami masalah ini jadi saya menggunakan kombinasi teknik yang tercantum pada pertanyaan StackOverflow . Berikut ini adalah apa yang saya gunakan untuk menyimpulkan variabel PATH sebenarnya yang telah ditetapkan, karena saya tidak ingin memodifikasi skrip dasar.

    tmppath=(${PATH// /@})
    array=(${tmppath//:/ })
    for i in "${array[@]//@/ }"
    do
        if ! [[ $PATH_NEW =~ "$i" ]]; then
            PATH_NEW="${PATH_NEW}$i:";
        fi
    done;
    PATH="${PATH_NEW%:}"
    export PATH
    unset PATH_NEW

Anda selalu dapat mengoptimalkan ini sedikit lebih banyak, tetapi saya memiliki kode tambahan dalam sumber asli saya untuk menampilkan apa yang terjadi untuk memastikan bahwa pengaturan variabel yang benar. Hal lain yang perlu diperhatikan adalah saya melakukan hal berikut

  1. ganti karakter SPACE dengan karakter @
  2. pisahkan array
  3. loop melalui array
  4. ganti setiap @ karakter dalam elemen string dengan spasi

Ini untuk memastikan bahwa saya dapat menangani direktori dengan spasi di (direktori home Samba dengan nama pengguna Active Directory dapat memiliki spasi!)

netniV
sumber
PERINGATAN: Implementasi ini memiliki bug utama !! itu akan menghapus /binjika ada entri lain seperti /usr/binkarena regexp cocok walaupun kedua entri tidak sama!
Sukima
Berikut ini adalah implementasi alternatif yang memperbaiki bug itu: github.com/sukima/dotfiles/blob/…
Sukima
1

Tetapkan jalur Anda secara eksplisit.

Cakemox
sumber
Terima kasih. Saya mencoba mengatur path secara eksplisit, tetapi saya memiliki file .bashrc yang saya gunakan di beberapa lingkungan, jadi default yang tepat PATHtidak selalu sama.
Christopher Bottoms
1

Hanya satu string:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"
bindbn
sumber
Terima kasih. Salah satu yang sulit ini akan menyebabkan saya adalah bahwa itu abjad (atau ASCIIbetically) menata ulang isi $PATH. Saya suka meletakkan direktori tertentu di awal $PATH(seperti /home/username) sehingga salinan pribadi saya yang dapat dieksekusi dijalankan alih-alih bawaan bawaan.
Christopher Bottoms
1

Saya dapat memikirkan dua cara berbeda untuk menyelesaikan ini. Yang pertama, adalah untuk memulai .bashrc Anda dengan garis yang secara eksplisit menetapkan PATH dasar Anda, dengan cara itu setiap kali Anda sumbernya, itu diatur ulang ke pangkalan sebelum menambahkan direktori tambahan.

Misalnya, tambahkan:

# Reset the PATH to prevent duplication and to make sure that we include
# everything we want.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Sebagai alternatif, Anda dapat memeriksa item sebelum menambahkannya ke jalur. Untuk melakukan itu, Anda akan menggunakan sesuatu seperti:

if ! [[ $PATH =~ '~/perl5/bin' ]]
then
    PATH="~/perl5/bin:$PATH"
fi

Yang terakhir cenderung mendapatkan sedikit repetitif jika Anda menambahkan banyak entri, jadi saya cenderung tetap dengan yang pertama. Jika Anda ingin menggunakan ini dan berencana menambahkan banyak entri, menulis fungsi bash untuk menanganinya akan lebih bijak.

Catatan: Opsi kedua hanya dapat berfungsi seperti yang ditulis dalam versi modern bash. Dukungan ekspresi reguler bukan fitur Bourne Shell (/ bin / sh), dan mungkin tidak ada di shell lain. Juga, penggunaan tanda kutip mungkin tidak diperlukan atau bahkan dapat menyebabkan masalah pada beberapa versi terbaru dari bash.

Christopher Cashell
sumber
Terima kasih. Saya mencoba mengatur path secara eksplisit, tetapi saya memiliki file .bashrc yang saya gunakan di beberapa lingkungan, jadi default yang tepat PATHtidak selalu sama.
Christopher Bottoms
Anda masih bisa mengatasinya dengan memeriksa skrip untuk melihat apa nama host lokal Anda, dan kemudian mengatur jalur absolut Anda dengan tepat untuk server itu.
Christopher Cashell
Hanya salah ketik: hilang dalam if. Juga, secara mengejutkan (bagi saya) mudah untuk menjadikannya sebuah fungsi yang melompati argumen yang dipisahkan oleh ruang, jadi Anda bisa mengatakan: add_to_PATH ~/perl5/bin ~/.bin unix.stackexchange.com/a/4973/28760 (saya kira casepernyataan yang digunakan lebih portabel) , tetapi Anda iflebih jelas bagi saya). EDIT kebetulan aneh bahwa pertanyaan itu juga menambah perl5!
13ren
@ 13ren: Terima kasih atas catatannya. Saya juga baru menyadari bahwa saya menggunakan tanda kutip tunggal pada baris yang ditetapkan PATH, daripada tanda kutip ganda seperti yang seharusnya saya miliki. Seperti yang tertulis, itu akan mengganti jalur yang ada dengan nama variabel. Ups! Tampaknya itu adalah entri yang buruk bagi saya untuk kesalahan ketik; keduanya sudah diperbaiki sekarang.
Christopher Cashell
0

Inilah solusi saya: PATH=$(echo -n $PATH | awk -v RS=: -v ORS=: '!x[$0]++' | sed "s/\(.*\).\{1\}/\1/")

Satu liner mudah yang bagus yang tidak meninggalkan jejak:

AJ.
sumber