Mục đích của [-n $ $ PS1] trong bashrc


10

Mục đích gì hiện [ -n "$PS1" ]tại [ -n "$PS1" ] && source ~/.bash_profile;phục vụ? Dòng này được bao gồm trong một repo.bashrc dotfiles .

Câu trả lời:


20

Đây là kiểm tra xem vỏ có tương tác hay không. Trong trường hợp này, chỉ tìm nguồn cung cấp ~/.bash_profiletệp nếu shell tương tác.

Xem "Đây có phải là Shell tương tác?" trong hướng dẫn bash, trong đó trích dẫn thành ngữ cụ thể đó. (Nó cũng khuyên bạn nên kiểm tra xem shell có tương tác hay không bằng cách kiểm tra xem $-biến đặc biệt có chứa iký tự hay không, đây là cách tiếp cận tốt hơn cho vấn đề này.)


bash, ít nhất, sẽ hủy đặt PS1 và PS2 nếu shell tương tác . Bạn có thể thấy rằng cho chính mình, với ( export PS1='abc$ '; bash -c 'echo "[$PS1]"' ), chỉ đơn giản là in []. Có vẻ như zsh không làm như vậy, ít nhất là từ một thử nghiệm ... Trong mọi trường hợp, mục đích của việc [ -n "$PS1" ]này là kiểm tra xem vỏ có tương tác hay không.
filbranden

3
Điều đó bashhủy cài đặt PS1 khi không tương tác (lỗi chính tả trong nhận xét trước của bạn) là một lỗi IMO, PS1 không phải là một biến cụ thể của bash, nó không có doanh nghiệp nào làm phiền nó. Đây là lớp vỏ duy nhất thực hiện điều đó (mặc dù yashcũng đặt PS1thành giá trị mặc định ngay cả khi không tương tác).
Stéphane Chazelas

1
Vì mã trong câu hỏi nằm trong một tệp cụ thể bash, nên đây có vẻ là một câu trả lời hợp lý. Các câu trả lời khác giải quyết trường hợp tổng quát hơn về thông số kỹ thuật POSIX hoặc các vỏ khác. Bạn đã trả lời những gì mục đích của việc này là gì? ý định trong Câu hỏi trong khi thích hợp để dành phần còn lại. Thật tốt khi biết bash đang làm gì và cũng có thể là cách tốt hơn để hoàn thành mục tiêu.
Jeff Schaller

Tôi sẽ xem xét câu trả lời này đầy đủ hơn nếu nó gợi ý một sự thay thế đáng tin cậy hơn (giả sử [[ $- = *i* ]] && source ~/.bash_profile).
Charles Duffy

@CharlesDuffy Thành thật tôi không nghĩ có nhiều sai sót [ -n "${PS1}" ], nhưng tôi vẫn cập nhật câu trả lời của mình để làm nổi bật rằng hướng dẫn bash cũng gợi ý / khuyên bạn nên kiểm tra $-để xác định xem vỏ có tương tác hay không, tôi hy vọng bạn thấy điều đó giúp cải thiện câu trả lời. Chúc mừng!
filbranden

19

Cái này làm gì

Đây là một cách rộng rãi để kiểm tra xem vỏ có tương tác hay không. Coi chừng nó chỉ hoạt động trong bash, nó không hoạt động với các shell khác. Vì vậy, nó ổn (nếu ngớ ngẩn) cho .bashrc, nhưng nó sẽ không hoạt động .profile(được đọc bởi sh, và bash chỉ là một trong những triển khai có thể có của sh, và không phải là cách phổ biến nhất).

Tại sao nó hoạt động (chỉ trong bash!)

Một shell tương tác đặt biến shellPS1 thành chuỗi dấu nhắc mặc định. Vì vậy, nếu lớp vỏ tương tác, PS1được đặt (trừ khi người dùng .bashrcđã gỡ bỏ nó, điều này chưa thể xảy ra ở đầu .bashrcvà bạn có thể coi đó là một việc ngớ ngẩn dù sao đi nữa).

Điều ngược lại là đúng trong bash: các trường hợp không tương tác của bash unset PS1khi chúng bắt đầu. Lưu ý rằng hành vi này là cụ thể đối với bash và được cho là một lỗi (tại sao nó bash -c '… do stuff with $var…'không hoạt động khi varPS1?). Nhưng tất cả các phiên bản bash lên đến và bao gồm 4.4 (phiên bản mới nhất khi tôi viết) làm điều này.

Nhiều hệ thống xuất khẩu PS1ra môi trường. Đó là một ý tưởng tồi, bởi vì nhiều shell khác nhau sử dụng PS1nhưng với một cú pháp khác nhau (ví dụ: thoát nhanh chóng của bash hoàn toàn khác với thoát nhanh chóng của zsh ). Nhưng nó đủ phổ biến đến mức trong thực tế, thấy rằng nó PS1được đặt không phải là một chỉ số đáng tin cậy cho thấy vỏ có tính tương tác. Vỏ có thể được thừa hưởng PS1từ môi trường.

Tại sao nó (mis) được sử dụng ở đây

.bashrclà tập tin bash đọc khi khởi động khi nó tương tác. Một thực tế ít được biết đến là bash cũng đọc .bashrclà một vỏ đăng nhập và các heuristic của bash kết luận rằng đây là một phiên từ xa (bash kiểm tra xem cha mẹ của nó là rshdhay sshd). Trong trường hợp thứ hai này, không chắc là nó PS1sẽ được đặt trong môi trường, vì chưa có tệp chấm nào chạy được.

Tuy nhiên, cách mã sử dụng thông tin này là phản tác dụng.

  • Nếu shell là shell tương tác, thì cái này chạy .bash_profiletrong shell đó. Nhưng .bash_profilelà một kịch bản thời gian đăng nhập. Nó có thể chạy một số chương trình chỉ được chạy một lần mỗi phiên. Nó có thể ghi đè một số biến môi trường mà người dùng đã cố tình đặt thành một giá trị khác trước khi chạy shell đó. Chạy .bash_profiletrong một vỏ không đăng nhập là gây rối.
  • Nếu shell là shell đăng nhập từ xa không tương tác, nó sẽ không tải .bash_profile. Nhưng đây là trường hợp tải .bash_profilecó thể hữu ích, vì vỏ đăng nhập không tương tác không tự động tải /etc/profile~/.profile.

Tôi nghĩ lý do mọi người làm điều này là vì người dùng đăng nhập thông qua GUI (một trường hợp rất phổ biến) và những người đặt cài đặt biến môi trường của họ .bash_profilethay vì .profile. Hầu hết các cơ chế đăng nhập GUI đều gọi .profilenhưng không .bash_profile(đọc .bash_profilesẽ yêu cầu chạy bash như một phần của phiên khởi động, thay vì sh). Với cấu hình này, khi người dùng mở một thiết bị đầu cuối, họ sẽ nhận được các biến môi trường. Tuy nhiên, người dùng sẽ không nhận được các biến môi trường của họ trong các ứng dụng GUI, đây là một nguồn gây nhầm lẫn rất phổ biến. Giải pháp ở đây là sử dụng .profilethay vì .bash_profileđặt các biến môi trường. Thêm một cầu nối giữa .bashrc.bash_profiletạo ra nhiều vấn đề hơn nó giải quyết.

Làm gì để thay thế

Có một cách đơn giản, di động để kiểm tra xem trình bao hiện tại có tương tác hay không: kiểm tra xem tùy chọn -icó được bật hay không.

case $- in
  *i*) echo "This shell is interactive";;
  *) echo "This shell is not interactive";;
esac

Điều này rất hữu ích .bashrckhi chỉ đọc .profilenếu shell không tương tác - tức là ngược lại với những gì mã làm! Đọc .profilenếu bash là vỏ đăng nhập (không tương tác) và không đọc nó nếu đó là vỏ tương tác.

if [[ $- != *i* && -r ~/.profile ]]; then . ~/.profile; fi

4
Có thể đáng chú ý rằng một cách tốt hơn để kiểm tra nếu một vỏ có tương tác với [[ -o interactive ]](ksh, bash, zsh) hoặc case $- in (*i*) ...; esac(POSIX)
Stéphane Chazelas

2
Bash của tôi (phiên bản 4.4.12) thực sự dường như không được đặt PS1nếu không chạy tương tác. Thật dễ dàng để kiểm tra: PS1=cuckoo bash -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'sẽ không in bất cứ thứ gì, trong khi PS1=cuckoo bash -i -c '[ -n "${PS1}" ] && echo "PS1=[${PS1}]"'in giá trị của $PS1tập hợp trong các tệp khởi động bash của bạn (nó sẽ không in chuỗi "cuckoo").
FooF

1
@ Stéphane Chazelas: POSIX không yêu cầu $-có chứa ivỏ tương tác.
schily

1
Bosh làm điều này từ năm 2012 để tương thích với ksh. Nó không được POSIX yêu cầu cho đến khi thay đổi mà bạn sử dụng có hiệu lực.
schily

1
Thành thật mà nói, tôi nói rằng gọi [ -n "${PS1}" ] sai là đi quá xa, sau tất cả, nó chỉ bị hỏng khi ai đó đang xuất khẩu PS1 (mà trong câu trả lời của bạn, bạn nói đó là một ý tưởng tồi và thậm chí đi vào lý do tại sao) và điều đó không ảnh hưởng bash anyways (vì nó hủy cài đặt PS1 và PS2 nếu shell không tương tác.) Có thể sử dụng một từ như "không khuyến khích" hoặc nói về "giới hạn" của phương pháp này sẽ tốt hơn. Tôi không nghĩ rằng nó "sai" hoàn toàn. Nếu có gì sai khi xuất PS1, đó là điều chắc chắn! Dù sao, cảm ơn vì đã đi vào chi tiết này.
filbranden

1

Có vẻ như khái niệm kỳ lạ này là kết quả từ thực tế bashkhông bắt đầu như một bản sao vỏ POSIX mà là một Bourne Shellbản sao.

Do đó, hành vi tương tác POSIX ( $ENVđược gọi là shell tương tác) đã được thêm vào sau đó bashvà không được biết đến rộng rãi.

Có một vỏ cho phép hành vi tương tự. Đây là cshvà csh tài trợ $promptcó giá trị cụ thể:

$prompt not set          non-interactive shell, test $?prompt.
$prompt set but == ""    .cshrc called by the which(1) command.
$prompt set and != ""    normal interactive shell.

Nhưng điều này không áp dụng cho vỏ Bourne cũng như vỏ POSIX.

Đối với hệ vỏ POSIX, phương thức được cấp duy nhất là đặt mã cho các vỏ tương tác vào tệp:

$ENV

có tên cụ thể vỏ. Nó là ví dụ

$HOME/.kshrc    for the korn shell
$HOME/.bashrc   for bash
$HOME/.mkshrc   for mksh
$HOME/.shrc     for the POSIX Bourne Shell

Những người khác đã đề cập đến cờ shell -i, nhưng điều này không thể sử dụng để lập trình đáng tin cậy. POSIX không yêu cầu set -ihoạt động, cũng không $-chứa ivỏ tương tác. POSIX chỉ yêu cầu sh -ithực thi shell vào chế độ tương tác.

Vì biến $PS1có thể được nhập từ môi trường, nên nó có thể có giá trị ngay cả trong chế độ không tương tác. Thực tế là bash unsets PS1trong bất kỳ shell không tương tác nào không được cấp bởi tiêu chuẩn và không được thực hiện bởi bất kỳ shell nào khác.

Vì vậy, lập trình sạch (thậm chí với bash) là đặt các lệnh cho shell tương tác vào $HOME/.bashrc.


0

Trước tiên tôi sẽ nói về Debian, và phần lớn thời gian Ubuntu cũng thiết lập cho bash. Và sau đó chạm vào các hệ thống khác.

Trong cài đặt shell start file có rất nhiều ý kiến.
Tôi cũng có ý kiến ​​của mình nhưng tôi sẽ cố gắng hiển thị các ví dụ hiện có về cài đặt chính xác.
Tôi sẽ sử dụng debuan vì khá dễ tìm thấy các ví dụ về các tệp của nó.
Và debian được sử dụng rất nhiều, vì vậy các cài đặt đã được kiểm tra tốt,

Mục tiêu của việc kiểm tra PS1 được đặt ra là gì?

Chỉ để tìm hiểu nếu vỏ là tương tác.

Các mặc định /etc/profiletrong debian và ubuntu (từ / usr / share / cơ sở-files / hồ sơ):

if [ "${PS1-}" ]; then
    if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then

Nếu đọc: if tương tác (bộ mặc định PS1) và nó là bash shell (nhưng không hoạt động như một mặc định sh), sau đó thay đổi PS1 thành một cái mới cụ thể (không phải mặc định).

Các mặc định /etc/bash.bashrctrong debian cũng chứa:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

Điều này khá rõ ràng trong những gì nó làm: Nếu tương tác không nguồn (phần còn lại).

Tuy nhiên, trong /etc/skel/.bashrclà một ví dụ về cách chính xác để kiểm tra trình bao tương tác (sử dụng $-):

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

Điều đó sẽ cho thấy rõ lý do tại sao của PS1 và một thay thế.

Đúng trình tự

Nên tránh cài đặt bạn đang báo cáo.
Trình tự (từ thiết lập hệ thống để thiết lập người dùng cụ thể hơn (ví bash)) là /etc/profile, /etc/bash.bashrc, ~/.profilevà cuối cùng ~/.bashrc. Điều đó đặt các hiệu ứng rộng nhất (và cho nhiều shell hơn) /etc/profile(thuộc sở hữu của root) theo sau /etc/bash.bashrc(cũng thuộc sở hữu của root) nhưng chỉ ảnh hưởng đến bash. Sau đó đến cài đặt cá nhân $HOME, đầu tiên là ~/.profilecho hầu hết các shell và ~/.bashrc(gần như tương đương ~/.bash_profile), chỉ dành riêng cho bash.

Do đó, sai lầm khi nguồn ~/.bashrctrong ~/.profile, nó được chuyển một người dùng cụ thể thiết lập cho bash đến một khái quát hơn đó là ảnh hưởng tới vỏ . Ngoại trừ nếu được thực hiện theo cách này :

# ~/.profile: executed by the command interpreter for login shells
# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

Nó kiểm tra xem bash đang chạy và chỉ tải .bashrcnếu đó là trường hợp.

Đây là một quyết định ngược dòng đến từ Debian. Lý do được giải thích ở đây .

Trên thực tế, điều ngược lại, tìm nguồn cung ứng ~/.profiletrong ~/.bash_profile(hoặc ~/.bashrc) chỉ áp dụng lại các quy tắc chung đã được tải cho một trường hợp sử dụng cụ thể và do đó "không tệ" (tôi không nói là "tốt"). Và tôi không nói tốt bởi vì nó có thể khiến việc tìm nguồn cung cấp các vòng lặp. Giống như khi một thư mục con tải một phụ huynh, đó là một vòng lặp thư mục.

Và trong nguồn cung cấp chéo này, việc kiểm tra vỏ tương tác có ý nghĩa. Chỉ khi một vỏ được tương tác được ~/.bashrctải, nhưng đến lượt nó có thể đang tải ~/.profile(hoặc ngược lại) và trong trường hợp này, việc kiểm tra vỏ tương tác có thể được sử dụng.

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.