Làm thế nào để thêm chính xác một đường dẫn đến PATH?


923

Tôi đang tự hỏi nơi một đường dẫn mới phải được thêm vào PATHbiến môi trường. Tôi biết điều này có thể được thực hiện bằng cách chỉnh sửa .bashrc(ví dụ), nhưng không rõ làm thế nào để làm điều này.

Cách này:

export PATH=~/opt/bin:$PATH

hay cái này

export PATH=$PATH:~/opt/bin

printf '\ nPATH = $ PATH: "đường dẫn để thêm" \ nexport PATH \ n' >> ~ / .bashrc
Sudoer


Nếu đã có một số đường dẫn được thêm vào, ví dụ PATH=$PATH:$HOME/.local/bin:$HOME/bin, một đường dẫn khác có thể được thêm bằng cách tách bằng a: vd PATH=$PATH:$HOME/.local/bin:$HOME/bin:/home/ec2-user/pear/bin.
Sandeepan Nath

2
Những câu trả lời này có hiệu quả với tất cả các hương vị của linux không?
Ungeheuer

Câu trả lời:


1033

Những thứ đơn giản

PATH=$PATH:~/opt/bin

hoặc là

PATH=~/opt/bin:$PATH

tùy thuộc vào việc bạn muốn thêm ~/opt/binvào cuối (để được tìm kiếm sau tất cả các thư mục khác, trong trường hợp có một chương trình có cùng tên trong nhiều thư mục) hoặc ở đầu (được tìm kiếm trước tất cả các thư mục khác).

Bạn có thể thêm nhiều mục cùng một lúc. PATH=$PATH:~/opt/bin:~/opt/node/binhoặc các biến thể về công việc đặt hàng chỉ là tốt. Đừng đặt exportở đầu dòng vì nó có các biến chứng bổ sung (xem bên dưới phần Ghi chú trên các vỏ khác không phải là bash).

Nếu bạn PATHđược xây dựng bởi nhiều thành phần khác nhau, bạn có thể kết thúc với các mục trùng lặp. Xem Làm thế nào để thêm đường dẫn thư mục chính được Unix phát hiện ra lệnh nào? Xóa các mục nhập $ PATH trùng lặp bằng lệnh awk để tránh thêm các mục trùng lặp hoặc xóa chúng.

Nhân tiện, một số bản phân phối sẽ tự động đưa ~/binvào PATH của bạn nếu nó tồn tại.

Đặt nó ở đâu

Đặt dòng để chỉnh sửa PATHtrong ~/.profile, hoặc trong ~/.bash_profilenếu đó là những gì bạn có.

Lưu ý rằng ~/.bash_rckhông được đọc bởi bất kỳ chương trình nào và ~/.bashrclà tệp cấu hình của các trường hợp tương tác của bash. Bạn không nên xác định các biến môi trường trong ~/.bashrc. Vị trí thích hợp để xác định các biến môi trường như PATH~/.profile(hoặc ~/.bash_profilenếu bạn không quan tâm đến các shell khác ngoài bash). Xem sự khác biệt giữa chúng và tôi nên sử dụng cái nào?

Đừng đặt nó vào /etc/environmenthoặc ~/.pam_environment: đây không phải là các tệp shell, bạn không thể sử dụng các thay thế như $PATHtrong đó. Trong các tệp này, bạn chỉ có thể ghi đè một biến, không thêm vào nó.

Biến chứng tiềm ẩn trong một số tập lệnh hệ thống

Bạn không cần exportnếu biến đã có trong môi trường: mọi thay đổi về giá trị của biến được phản ánh trong môi trường. PATHluôn luôn xuất hiện trong môi trường; tất cả các hệ thống unix thiết lập nó rất sớm (thường là trong quá trình đầu tiên, trên thực tế).

Tại thời điểm đăng nhập, bạn có thể dựa vào PATHviệc đã ở trong môi trường và đã chứa một số thư mục hệ thống. Nếu bạn đang viết một kịch bản có thể được thực hiện vào đầu trong khi thiết lập một số loại môi trường ảo, bạn có thể cần phải đảm bảo rằng PATHkhông bị để trống và xuất khẩu: nếu PATHvẫn còn unset, sau đó một cái gì đó như PATH=$PATH:/some/directorysẽ đặt PATHđể :/some/directory, và các thành phần rỗng ở đầu có nghĩa là thư mục hiện tại (như .:/some/directory).

if [ -z "${PATH-}" ]; then export PATH=/usr/local/bin:/usr/bin:/bin; fi

Ghi chú về vỏ khác với bash

Trong bash, ksh và zsh, exportlà cú pháp đặc biệt, và cả hai PATH=~/opt/bin:$PATHexport PATH=~/opt/bin:$PATHlàm điều đúng ngay cả. Trong các shell kiểu Bourne / POSIX khác, chẳng hạn như dấu gạch ngang ( /bin/shtrên nhiều hệ thống), exportđược phân tích cú pháp như một lệnh thông thường, hàm ý hai điểm khác biệt:

Vì vậy, trong các shell như dash, export PATH=~/opt/bin:$PATHthiết lập PATHchuỗi ký tự ~/opt/bin/:theo sau là giá trị PATHlên đến không gian đầu tiên. PATH=~/opt/bin:$PATH(một bài tập trần) không yêu cầu báo giá và thực hiện đúng. Nếu bạn muốn sử dụng exporttrong một tập lệnh di động, bạn cần phải viết export PATH="$HOME/opt/bin:$PATH", hoặc PATH=~/opt/bin:$PATH; export PATH(hoặc PATH=$HOME/opt/bin:$PATH; export PATHcho tính di động đối với cả vỏ Bourne không chấp nhận export var=valuevà không mở rộng dấu ngã).

¹ Đây là không đúng sự thật trong vỏ Bourne (như trong Bourne shell thực tế, chứ không phải hiện đại vỏ POSIX-style), nhưng bạn đang rất khó có khả năng gặp phải vỏ cũ như những ngày này.


Vẫn không thể hiểu được sự phức tạp với xuất khẩu. bạn có thể đơn giản hóa nó?
tiên tri

@priojeetpriyom Giải thích đơn giản: bạn không cần export.
Gilles

Cảm ơn bạn cho câu trả lời này, hoàn toàn chi tiết. Bạn nói " Bạn không nên xác định các biến môi trường trong ~ / .bashrc ", nhưng thật không may 100% các chương trình mà tôi đã cài đặt trên hệ thống của mình có sửa đổi đường dẫn (FZF và Rust's Cargo) sửa đổi đường dẫn trong .bashrc. Tôi giả sử vì FZF được viết bằng Rust, nó cũng theo mô hình của Rust.
icc97

83

Dù bằng cách nào, nhưng chúng không làm điều tương tự: các yếu tố PATHđược kiểm tra từ trái sang phải. Trong ví dụ đầu tiên của bạn, các tệp thực thi ~/opt/binsẽ có quyền ưu tiên so với các tệp được cài đặt, ví dụ, trong /usr/bin, có thể có hoặc không phải là thứ bạn muốn.

Đặc biệt, từ quan điểm an toàn, sẽ rất nguy hiểm khi thêm đường dẫn vào phía trước, bởi vì nếu ai đó có thể có quyền truy cập ghi vào của bạn ~/opt/bin, ví dụ, họ có thể đặt một địa chỉ khác ls, trong đó bạn có thể sử dụng thay thế của /bin/lsmà không nhận thấy. Bây giờ hãy tưởng tượng tương tự cho sshhoặc trình duyệt hoặc lựa chọn của bạn ... (Điều tương tự cũng xảy ra ba lần khi đưa vào đường dẫn của bạn.)


6
Nhưng nếu bạn muốn có phiên bản tùy chỉnh của riêng mình ls, bạn cần đặt nó trong một thư mục trước /bin.
Barmar

16
hoặc bí danh ls = myls
waltinator

36

Tôi bối rối bởi câu hỏi 2 (vì đã bị xóa khỏi câu hỏi vì đó là do vấn đề không liên quan):

Cách nào khả thi để nối thêm đường dẫn trên các dòng khác nhau? Ban đầu tôi nghĩ rằng điều này có thể thực hiện các mẹo:

export PATH=$PATH:~/opt/bin
export PATH=$PATH:~/opt/node/bin

nhưng không phải vì nhiệm vụ thứ hai không chỉ nối thêm ~/opt/node/binmà còn toàn bộ PATHnhiệm vụ trước đó.

Đây là một cách giải quyết có thể:

export PATH=$PATH:~/opt/bin:~/opt/node/bin

nhưng để dễ đọc, tôi muốn có một nhiệm vụ cho một đường dẫn.

Nếu bạn nói

PATH=~/opt/bin

đó là tất cả những gì sẽ có trong ĐƯỜNG của bạn. PATH chỉ là một biến môi trường và nếu bạn muốn thêm vào PATH, bạn phải xây dựng lại biến với chính xác nội dung bạn muốn. Đó là, những gì bạn đưa ra làm ví dụ cho câu hỏi 2 chính xác là những gì bạn muốn làm, trừ khi tôi hoàn toàn thiếu quan điểm của câu hỏi.

Tôi sử dụng cả hai hình thức trong mã của tôi. Tôi có một hồ sơ chung mà tôi cài đặt trên mọi máy tôi làm việc trông giống như thế này, để phù hợp với các thư mục có khả năng bị thiếu:

export PATH=/opt/bin:/usr/local/bin:/usr/contrib/bin:/bin:/usr/bin:/usr/sbin:/usr/bin/X11
# add optional items to the path
for bindir in $HOME/local/bin $HOME/bin; do
    if [ -d $bindir ]; then
        PATH=$PATH:${bindir}
    fi
done

2
Bạn đã đúng về ví dụ của câu hỏi 2, nó hoạt động. Một vấn đề khác liên quan đến PATH trên hệ thống của tôi làm tôi bối rối. Xin lỗi vì điều đó.
Paolo

26

Cách chống đạn của việc bổ sung / chuẩn bị

Có rất nhiều cân nhắc liên quan đến việc lựa chọn nối thêm so với trả trước. Nhiều trong số chúng được bao phủ trong các câu trả lời khác, vì vậy tôi sẽ không lặp lại chúng ở đây.

Một điểm quan trọng là, ngay cả khi các tập lệnh hệ thống không sử dụng điều này (tôi tự hỏi tại sao) * 1 , cách chống đạn để thêm đường dẫn (ví dụ $HOME/bin:) vào biến môi trường PATH là

PATH="${PATH:+${PATH}:}$HOME/bin"

để nối thêm (thay vì PATH="$PATH:$HOME/bin") và

PATH="$HOME/bin${PATH:+:${PATH}}"

để trả trước (thay vì PATH="$HOME/bin:$PATH")

Điều này tránh được dấu hai chấm giả / dấu đầu dòng khi $PATHban đầu trống rỗng, có thể có tác dụng phụ không mong muốn và có thể trở thành cơn ác mộng , khó tìm thấy ( câu trả lời này giải quyết ngắn gọn về trường hợp này awk).

Giải thích (từ Mở rộng tham số Shell ):

${parameter:+word}

Nếu parameterlà null hoặc unset, không có gì được thay thế, nếu không thì việc mở rộng wordđược thay thế.

Do đó, ${PATH:+${PATH}:}được mở rộng thành: 1) không có gì, nếu PATHlà null hoặc unset, 2) ${PATH}:, nếu PATHđược đặt.

Lưu ý : Đây là cho bash.


* 1 Tôi vừa thấy rằng các tập lệnh như devtoolset-6/enablethực sự sử dụng cái này,

$ cat /opt/rh/devtoolset-6/enable
# General environment variables
export PATH=/opt/rh/devtoolset-6/root/usr/bin${PATH:+:${PATH}}
...

24

Linux xác định đường dẫn tìm kiếm thực thi với $PATHbiến môi trường. Để thêm thư mục / dữ liệu / myscripts vào đầu $PATHbiến môi trường, hãy sử dụng như sau:

PATH=/data/myscripts:$PATH

Để thêm thư mục đó vào cuối đường dẫn, sử dụng lệnh sau:

PATH=$PATH:/data/myscripts

Nhưng điều trước là không đủ vì khi bạn đặt biến môi trường bên trong tập lệnh, thay đổi đó chỉ có hiệu lực trong tập lệnh. Chỉ có hai cách xung quanh giới hạn này:

  • Nếu trong tập lệnh, bạn xuất biến môi trường thì nó có hiệu lực trong bất kỳ chương trình nào được gọi bởi tập lệnh. Lưu ý rằng nó không hiệu quả trong chương trình được gọi là script.
  • Nếu chương trình gọi tập lệnh thực hiện bằng cách đưa vào thay vì gọi, bất kỳ thay đổi môi trường nào trong tập lệnh đều có hiệu lực trong chương trình gọi. Việc đưa vào như vậy có thể được thực hiện bằng lệnh dot hoặc lệnh nguồn.

Ví dụ:

$HOME/myscript.sh
source $HOME/myscript.sh

Về cơ bản, việc kết hợp kịch bản "được gọi" trong tập lệnh "gọi". Nó giống như một #incoide trong C. Vì vậy, nó có hiệu quả trong tập lệnh hoặc chương trình "gọi". Nhưng tất nhiên, nó không hiệu quả trong bất kỳ chương trình hoặc tập lệnh nào được gọi bởi chương trình gọi. Để làm cho nó hiệu quả trong suốt chuỗi cuộc gọi, bạn phải tuân theo cài đặt của biến môi trường bằng lệnh xuất.

Ví dụ, chương trình bash shell kết hợp nội dung của tệp .bash_profile bằng cách đưa vào. Đặt 2 dòng sau vào .bash_profile:

PATH=$PATH:/data/myscripts
export PATH

có hiệu quả đặt 2 dòng mã đó trong chương trình bash. Vì vậy, trong bash, biến $ PATH bao gồm $HOME/myscript.shvà do câu lệnh xuất, bất kỳ chương trình nào được gọi bởi bash đều có $PATHbiến bị thay đổi . Và bởi vì bất kỳ chương trình nào bạn chạy từ dấu nhắc bash đều được gọi bằng bash, đường dẫn mới có hiệu lực đối với mọi thứ bạn chạy từ dấu nhắc bash.

Điểm mấu chốt là để thêm một thư mục mới vào đường dẫn, bạn phải nối thêm hoặc thêm thư mục vào biến môi trường $ PATH trong tập lệnh có trong shell và bạn phải xuất $PATHbiến môi trường.

Thêm thông tin ở đây


19

Lâu nay tôi đã giữ cho tôi hai chức năng pathaddpathrmhỗ trợ thêm các yếu tố vào đường dẫn mà không cần phải lo lắng về sự trùng lặp.

pathaddnhận một đối số đường dẫn duy nhất và một afterđối số tùy chọn mà nếu được cung cấp sẽ nối với mặt PATHkhác, nó sẽ bổ sung cho nó.

Trong hầu hết mọi tình huống nếu bạn thêm vào đường dẫn thì có khả năng bạn muốn ghi đè mọi thứ đã có trong đường dẫn, đó là lý do tại sao tôi chọn không trả trước theo mặc định.

pathadd() {
    newelement=${1%/}
    if [ -d "$1" ] && ! echo $PATH | grep -E -q "(^|:)$newelement($|:)" ; then
        if [ "$2" = "after" ] ; then
            PATH="$PATH:$newelement"
        else
            PATH="$newelement:$PATH"
        fi
    fi
}

pathrm() {
    PATH="$(echo $PATH | sed -e "s;\(^\|:\)${1%/}\(:\|\$\);\1\2;g" -e 's;^:\|:$;;g' -e 's;::;:;g')"
}

Đặt chúng trong bất kỳ tập lệnh nào bạn muốn thay đổi môi trường PATH và bây giờ bạn có thể làm.

pathadd "/foo/bar"
pathadd "/baz/bat" after
export PATH

Bạn được đảm bảo không thêm vào đường dẫn nếu nó đã ở đó. Nếu bây giờ bạn muốn đảm bảo /baz/batlà lúc bắt đầu.

pathrm "/baz/bat"
pathadd "/baz/bat"
export PATH

Bây giờ bất kỳ đường dẫn nào cũng có thể được di chuyển ra phía trước nếu nó đã ở trong đường dẫn mà không cần nhân đôi.


Cách tiếp cận liên quan và sạch hơn để kiểm tra sự hiện diện của một thư mục trong PATH của bạn: unix.stackexchange.com/a/32054/135943
Wildcard

9

Tôi không thể nói cho các bản phân phối khác, nhưng Ubuntu có một tệp, / etc / môi trường, đó là đường dẫn tìm kiếm mặc định cho tất cả người dùng. Vì máy tính của tôi chỉ được tôi sử dụng, tôi đặt bất kỳ thư mục nào tôi muốn trong đường dẫn của mình ở đó, trừ khi đó là một bổ sung tạm thời mà tôi đặt trong một tập lệnh.


6

Có một số tình huống sử dụng nó PATH=/a/b:$PATHcó thể được coi là cách "không chính xác" để thêm đường dẫn đến PATH:

  1. Thêm một đường dẫn không thực sự là một thư mục.
  2. Thêm một đường dẫn đã ở PATHdạng tương tự.
  3. Thêm một đường dẫn tương đối (vì thư mục thực tế được tìm kiếm sẽ thay đổi khi bạn thay đổi thư mục làm việc hiện tại).
  4. Thêm một đường dẫn đã ở PATHdạng khác (nghĩa là bí danh do sử dụng liên kết tượng trưng hoặc ..).
  5. Nếu bạn tránh thực hiện 4, không di chuyển đường dẫn ra phía trước PATHkhi nó có ý định ghi đè các mục khác PATH.

Hàm này (chỉ dành cho Bash) thực hiện "điều đúng" trong các tình huống trên (với một ngoại lệ, xem bên dưới), trả về mã lỗi và in các thông điệp hay cho con người. Mã lỗi và thông báo có thể bị vô hiệu hóa khi chúng không muốn.

prepath() {
    local usage="\
Usage: prepath [-f] [-n] [-q] DIR
  -f Force dir to front of path even if already in path
  -n Nonexistent dirs do not return error status
  -q Quiet mode"

    local tofront=false errcode=1 qecho=echo
    while true; do case "$1" in
        -f)     tofront=true;       shift;;
        -n)     errcode=0;          shift;;
        -q)     qecho=':';          shift;;
        *)      break;;
    esac; done
    # Bad params always produce message and error code
    [[ -z $1 ]] && { echo 1>&2 "$usage"; return 1; }

    [[ -d $1 ]] || { $qecho 1>&2 "$1 is not a directory."; return $errcode; }
    dir="$(command cd "$1"; pwd -P)"
    if [[ :$PATH: =~ :$dir: ]]; then
        $tofront || { $qecho 1>&2 "$dir already in path."; return 0; }
        PATH="${PATH#$dir:}"        # remove if at start
        PATH="${PATH%:$dir}"        # remove if at end
        PATH="${PATH//:$dir:/:}"    # remove if in middle
    fi
    PATH="$dir:$PATH"
}

Ngoại lệ là chức năng này không hợp quy hóa các đường dẫn được thêm vào PATHthông qua các phương tiện khác, vì vậy nếu có một bí danh không chính tắc cho một đường dẫn PATH, điều này sẽ thêm một bản sao. Cố gắng chuẩn hóa các đường dẫn đã có PATHlà một đề xuất khó khăn vì một đường dẫn tương đối có ý nghĩa rõ ràng khi được chuyển đến prepathnhưng khi đã ở trong đường dẫn, bạn không biết thư mục làm việc hiện tại là gì khi được thêm vào.


liên quan đến các đường dẫn tương đối: những gì về việc có một công tắc '-r', sẽ thêm đường dẫn mà không làm cho nó tuyệt đối trước, và điều này cũng sẽ tìm kiếm nó là tuyệt đối trước khi thêm nó? Nếu đây là kịch bản, người ta có thể sử dụng nó trong các shell khác. Có bất kỳ lợi ích của việc có nó như là một chức năng? mã đẹp!
hoijui

1
@hoijui Nó phải là một chức năng vì nó sửa đổi môi trường hiện tại. Nếu nó là một tập lệnh, nó sẽ sửa đổi môi trường của quy trình con đang chạy tập lệnh và khi tập lệnh thoát ra, bạn có giống $PATHnhư trước đây. Đối với -r, không, tôi nghĩ rằng các đường dẫn tương đối trong $PATHquá không đáng tin cậy và kỳ lạ (đường dẫn của bạn thay đổi mỗi khi bạn cd!) Để muốn hỗ trợ một cái gì đó như thế trong một công cụ chung.
Curt J. Sampson

5

Đối với tôi (trên Mac OS X 10.9.5), việc thêm tên đường dẫn (ví dụ /mypathname) vào tệp /etc/pathshoạt động rất tốt.

Trước khi chỉnh sửa, echo $PATHtrả về:

/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin

Sau khi chỉnh sửa /etc/pathsvà khởi động lại shell, biến $ PATH được thêm vào /pathname. Thật vậy, echo $PATHtrả về:

/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/mypathname

Điều gì đã xảy ra là /mypathnameđã được thêm vào $PATHbiến.


3
Tốt hơn là thêm một tệp vào thư mục /etc/paths.d hơn là chỉnh sửa tệp / etc / path.
rbrewer

4

Để thêm một đường dẫn mới vào PATHbiến môi trường:

export PATH=$PATH:/new-path/

Để thay đổi này được áp dụng cho mọi shell bạn mở, hãy thêm nó vào tệp mà shell sẽ lấy khi nó được gọi. Trong các shell khác nhau, điều này có thể là:

  • Bash Shell: ~ / .bash_profile, ~ / .bashrc hoặc hồ sơ
  • Korn Shell: ~ / .kshrc hoặc .profile
  • Vỏ Z: ~ / .zshrc hoặc .zprofile

ví dụ

# export PATH=$PATH:/root/learning/bin/
# source ~/.bashrc
# echo $PATH

Bạn có thể thấy đường dẫn được cung cấp trong đầu ra ở trên.


4

Đâ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 :


1
-bash: awk: Không có tập tin hoặc thư mục như vậy -bash: sed: Không có tập tin hoặc thư mục như vậy
davidcondrey

1
@davidcondrey - awk và sed là các lệnh bên ngoài rất phổ biến. Câu trả lời này cung cấp một cách thuần túy để đạt được điều tương tự, vì vậy nó hoạt động ngay cả trong trường hợp khi không có awk và / hoặc sed (hoặc các thư mục tương ứng của chúng không nằm trong đường dẫn!)
sancho.s
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.