Cắt tỉa các mục trùng lặp từ biến PATH


9

Tôi sửa đổi .bashrc của tôi thường xuyên và sau đó nguồn nó. Tuy nhiên, khi tôi có những thứ như export PATH="~/bin:~/perl5/bin:$PATH"trong tệp của mình, thì PATHbiến môi trường sẽ tăng lên mỗi khi tôi lấy tệp.

Ví dụ: lần đầu tiên .bashrc có nguồn gốc, PATHbiến bao gồm ~/bin:~/perl5/bin:/usr/bin:/bin.

Lần thứ hai nó bao gồm ~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Lần thứ ba nó bao gồm ~/bin:~/perl5/bin:~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Có cách nào đơn giản để làm cho nó chỉ thêm bất cứ thứ gì chưa có trong PATH không?

Câu trả lời:


12

Sử dụng hàm pathmunge () có sẵn trong hầu hết các bản phân phối /etc/profile:

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

chỉnh sửa : Đối với zshngười dùng, typeset -U <variable_name>sẽ lặp lại các mục đường dẫn.


Cảm ơn! Nó không có trong /etc/profileDebian Lenny, vì vậy tôi đưa nó vào .bashrc.
Christopher Bottoms

Cách sử dụng: pathmunge /some/pathsẽ diễn ra /some/pathvào đầu $PATHpathmunge /some/path aftersẽ diễn ra /some/pathvào cuối$PATH
Christopher Bottoms

Cách kiểm tra sạch hơn để xem liệu thư mục hew có tồn tại trong đường dẫn hiện tại không, trong shell bash hiện đại : if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then.
Christopher Cashell

Tôi sẽ không sử dụng cái này. Nếu phiên bản ngây thơ của tôi nói PATH = / foo: $ PATH có nghĩa là tôi muốn / foo giành chiến thắng. pathmunge /foo beforekhông hoàn thành nó Một cách tốt hơn sẽ là thêm / foo ở đầu và sau đó loại bỏ các bản sao sau đó.
Don nở

3

Tôi đã gặp vấn đề này vì vậy tôi đã sử dụng kết hợp các kỹ thuật được liệt kê trong câu hỏi StackOverflow . Sau đây là những gì tôi đã sử dụng để khấu trừ biến PATH thực tế đã được đặt, vì tôi không muốn sửa đổi tập lệnh cơ sở.

    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

Bạn luôn có thể tối ưu hóa điều này nhiều hơn một chút, nhưng tôi có thêm mã trong bản gốc của mình để hiển thị những gì đang xảy ra để đảm bảo rằng nó được đặt chính xác các biến. Một điều khác cần lưu ý là tôi thực hiện như sau

  1. thay thế bất kỳ ký tự SPACE nào bằng ký tự @
  2. tách mảng
  3. lặp qua mảng
  4. thay thế bất kỳ ký tự @ nào trong chuỗi phần tử bằng dấu cách

Điều này là để đảm bảo rằng tôi có thể xử lý các thư mục có khoảng trắng trong (thư mục chính của Samba với tên người dùng Active Directory có thể có khoảng trắng!)


CẢNH BÁO: Việc thực hiện này có một lỗi lớn !! nó sẽ xóa /binnếu có các mục khác giống như /usr/binvì biểu thức chính quy phù hợp mặc dù hai mục không giống nhau!
Sukima

Đây là một triển khai thay thế giúp khắc phục lỗi đó: github.com/sukima/dotfiles/blob/ mẹo
Sukima

1

Đặt đường dẫn của bạn một cách rõ ràng.


Cảm ơn. Tôi đã cố gắng thiết lập đường dẫn rõ ràng, nhưng tôi có tệp .bashrc mà tôi sử dụng trong nhiều môi trường, vì vậy mặc định chính xác PATHkhông phải lúc nào cũng giống nhau.
Christopher Bottoms

1

Chỉ một chuỗi:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"

Cảm ơn. Một điều khó khăn này sẽ gây ra cho tôi là nó theo thứ tự bảng chữ cái (hoặc ASCIIbively) sắp xếp lại nội dung của $PATH. Tôi muốn đặt các thư mục cụ thể vào đầu $PATH(như /home/username) để các bản sao thực thi cá nhân của tôi được chạy thay vì mặc định tích hợp.
Christopher Bottoms

1

Tôi có thể nghĩ về hai cách khác nhau mà bạn có thể giải quyết điều này. Cách đầu tiên, là khởi động .bashrc của bạn bằng một dòng rõ ràng đặt PATH cơ sở của bạn, theo cách đó mỗi khi bạn lấy nguồn, nó được đặt lại về cơ sở trước khi thêm các thư mục bổ sung.

Ví dụ: thêm:

# 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

Thay phiên, bạn có thể kiểm tra một mục trước khi bạn thêm nó vào đường dẫn. Để làm điều đó, bạn sẽ sử dụng một cái gì đó như:

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

Tuy nhiên, cái sau có xu hướng lặp đi lặp lại một chút nếu bạn thêm nhiều mục, tuy nhiên, vì vậy tôi có xu hướng gắn bó với mục trước. Nếu bạn muốn sử dụng cái này và lên kế hoạch thêm rất nhiều mục, viết một hàm bash để xử lý nó sẽ là điều khôn ngoan.

Lưu ý: Tùy chọn thứ hai chỉ có thể hoạt động như được viết trong bash phiên bản hiện đại. Hỗ trợ biểu thức chính quy không phải là tính năng Bourne Shell (/ bin / sh) và có thể không tồn tại trong các shell khác. Ngoài ra, việc sử dụng dấu ngoặc kép có thể không cần thiết hoặc thậm chí có thể gây ra sự cố trên một số phiên bản mới nhất của bash.


Cảm ơn. Tôi đã cố gắng thiết lập đường dẫn rõ ràng, nhưng tôi có tệp .bashrc mà tôi sử dụng trong nhiều môi trường, vì vậy mặc định chính xác PATHkhông phải lúc nào cũng giống nhau.
Christopher Bottoms

Bạn vẫn có thể xử lý điều đó bằng cách kiểm tra tập lệnh để xem tên máy chủ cục bộ của bạn là gì, sau đó đặt đường dẫn tuyệt đối của bạn cho máy chủ đó.
Christopher Cashell

Chỉ là một lỗi đánh máy: bị thiếu trong if. Ngoài ra, thật đáng ngạc nhiên (với tôi) dễ dàng biến điều này thành một hàm lặp lại các đối số được phân tách bằng dấu cách, vì vậy bạn chỉ cần nói: add_to_PATH ~/perl5/bin ~/.bin unix.stackexchange.com/a/4973/28760 (Tôi đoán rằng casecâu lệnh được sử dụng có tính di động cao hơn , nhưng bạn ifrõ ràng hơn với tôi). EDIT trùng hợp kỳ lạ mà câu hỏi đó cũng thêm perl5!
13ren

@ 13ren: Cảm ơn bạn đã lưu ý. Tôi cũng chỉ nhận ra rằng tôi đã sử dụng dấu ngoặc đơn trong dòng thiết lập PATH, thay vì dấu ngoặc kép như tôi nên có. Như đã viết, nó sẽ thay thế đường dẫn hiện tại của bạn bằng tên biến. Giáo sư! Rõ ràng đó là một mục xấu cho tôi về lỗi chính tả; cả hai đều cố định
Christopher Cashell

0

Đây là giải pháp của tôi: PATH=$(echo -n $PATH | awk -v RS=: -v ORS=: '!x[$0]++' | sed "s/\(.*\).\{1\}/\1/")

Một lớp lót dễ dàng đẹp mà không để lại dấu vết:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.