Tại sao bashrc kiểm tra xem shell hiện tại có tương tác không?


62

Trên bản cài đặt Arch của tôi /etc/bash.bashrc/etc/skel/.bashrcchứa các dòng này:

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

Trên Debian, /etc/bash.bashrccó:

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

/etc/skel/.bashrc:

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

Theo man bashTuy nhiên, vỏ không tương tác thậm chí không đọc những tập tin này:

   When  bash  is  started  non-interactively,  to run a shell script, for
   example, it looks for the variable BASH_ENV in the environment, expands
   its  value if it appears there, and uses the expanded value as the name
   of a file to read and execute.  Bash behaves as if the  following  com
   mand were executed:
          if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
   but  the value of the PATH variable is not used to search for the file
   name.

Nếu tôi hiểu chính xác, các *.bashrctệp sẽ chỉ được đọc nếu BASH_ENVđược đặt để trỏ đến chúng. Đây là điều không thể xảy ra do tình cờ và sẽ chỉ xảy ra nếu ai đó đã đặt rõ ràng biến đó cho phù hợp.

Điều đó dường như phá vỡ khả năng có các tập lệnh .bashrctự động tạo nguồn cho người dùng bằng cách cài đặt BASH_ENV, thứ gì đó có thể có ích. Cho rằng bash sẽ không bao giờ đọc các tệp này khi chạy không tương tác trừ khi được yêu cầu rõ ràng làm như vậy, tại sao các *bashrctệp mặc định không cho phép nó?


5
Để có câu trả lời thực tế, hãy xem SCP thất bại mà không có lỗi
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


69

Đây là một câu hỏi mà tôi sẽ gửi ở đây một vài tuần trước. Giống như terdon , tôi hiểu rằng a .bashrcchỉ có nguồn gốc cho các vỏ Bash tương tác nên không cần phải .bashrckiểm tra nếu nó đang chạy trong một vỏ tương tác. Một cách khó hiểu, tất cả các bản phân phối tôi sử dụng (Ubuntu, RHEL và Cygwin) đều có một số loại kiểm tra (thử nghiệm $-hoặc $PS1) để đảm bảo trình bao hiện tại có tương tác. Tôi không thích lập trình sùng bái hàng hóa vì vậy tôi bắt đầu tìm hiểu mục đích của mã này .bashrc.

Bash có một trường hợp đặc biệt cho đạn pháo từ xa

Sau khi nghiên cứu vấn đề, tôi phát hiện ra rằng vỏ từ xa được xử lý khác nhau. Mặc dù các shell Bash không tương tác thường không chạy ~/.bashrccác lệnh khi khởi động, một trường hợp đặc biệt được tạo khi shell được gọi bởi trình nền shell từ xa :

Bash cố gắng xác định khi nào nó đang được chạy với đầu vào tiêu chuẩn của nó được kết nối với kết nối mạng, như khi được thực thi bởi trình nền shell từ xa, thường rshdhoặc daemon shell an toàn sshd. Nếu Bash xác định nó đang được chạy theo kiểu này, nó sẽ đọc và thực thi các lệnh từ ~ / .bashrc, nếu tệp đó tồn tại và có thể đọc được. Nó sẽ không làm điều này nếu được gọi là sh. Các --norctùy chọn có thể được sử dụng để ngăn chặn hành vi này, và các --rcfiletùy chọn có thể được sử dụng để buộc một tập tin được đọc, nhưng không phải rshdvà cũng không sshdthường gọi vỏ với những tùy chọn hoặc cho phép họ được xác định.

Thí dụ

Chèn sau đây khi bắt đầu một điều khiển từ xa .bashrc. (Nếu .bashrccó nguồn gốc bởi .profilehoặc .bash_profile, tạm thời vô hiệu hóa điều này trong khi thử nghiệm):

echo bashrc
fun()
{
    echo functions work
}

Chạy các lệnh sau cục bộ:

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • Không itrong $-chỉ ra rằng vỏ là không tương tác .
  • Không hàng đầu -trong $0chỉ ra rằng vỏ không phải là một vỏ đăng nhập .

Các hàm Shell được xác định trong điều khiển từ xa .bashrccũng có thể được chạy:

$ ssh remote_host fun
bashrc
functions work

Tôi nhận thấy rằng ~/.bashrcchỉ có nguồn gốc khi một lệnh được quy định như là đối số cho ssh. Điều này có ý nghĩa: khi sshđược sử dụng để bắt đầu một vỏ đăng nhập thông thường .profilehoặc .bash_profileđược chạy (và .bashrcchỉ có nguồn gốc nếu được thực hiện một cách rõ ràng bởi một trong những tệp này).

Lợi ích chính mà tôi có thể thấy là có .bashrcnguồn gốc khi chạy lệnh từ xa (không tương tác) là các hàm shell có thể chạy được. Tuy nhiên, hầu hết các lệnh trong một điển hình .bashrcchỉ có liên quan trong một vỏ tương tác, ví dụ, các bí danh không được mở rộng trừ khi vỏ đó tương tác.

Chuyển tập tin từ xa có thể thất bại

Đây thường không phải là vấn đề khi rshhoặc sshđược sử dụng để bắt đầu một vỏ đăng nhập tương tác hoặc khi các vỏ không tương tác được sử dụng để chạy các lệnh. Tuy nhiên, nó có thể là một vấn đề cho các chương trình như rcp, scpsftpsử dụng vỏ từ xa để chuyển dữ liệu.

Hóa ra trình bao mặc định của người dùng từ xa (như Bash) được khởi động ngầm khi sử dụng scplệnh. Không có đề cập nào về điều này trong trang man - chỉ có một đề cập scpsử dụng sshcho việc truyền dữ liệu của nó. Điều này có hậu quả là nếu .bashrcchứa bất kỳ lệnh nào in ra đầu ra tiêu chuẩn, việc truyền tệp sẽ thất bại , ví dụ, scp không có lỗi .

Xem thêm báo cáo lỗi Red Hat liên quan này từ 15 năm trước, scp bị phá vỡ khi có lệnh echo trong / etc / bashrc (cuối cùng đã bị đóng WONTFIX).

Tại sao scpsftpthất bại

SCP (Bản sao bảo mật)SFTP (Giao thức truyền tệp an toàn) có các giao thức riêng cho các đầu cuối cục bộ và từ xa để trao đổi thông tin về (các) tệp được truyền. Bất kỳ văn bản bất ngờ nào từ đầu cuối đều bị hiểu sai là một phần của giao thức và quá trình chuyển không thành công. Theo một câu hỏi thường gặp từ Snail Book

Những gì thường xảy ra, tuy nhiên, được rằng có những báo cáo trong cả hai hệ thống hoặc các tập tin khởi động vỏ cho mỗi người dùng trên máy chủ ( .bashrc, .profile, /etc/csh.cshrc, .login, vv) mà tin nhắn văn bản đầu ra trên đăng nhập, dự định sẽ được đọc bởi con người (như fortune, echo "Hi there!", Vân vân.).

Mã như vậy chỉ nên tạo đầu ra trên thông tin đăng nhập tương tác, khi có ttygắn với đầu vào tiêu chuẩn. Nếu nó không thực hiện kiểm tra này, nó sẽ chèn các tin nhắn văn bản này vào nơi chúng không thuộc về: trong trường hợp này, làm ô nhiễm luồng giao thức giữa scp2/ sftpsftp-server.

Lý do các tập tin khởi động shell hoàn toàn có liên quan, đó là sshd sử dụng trình bao của người dùng khi khởi động bất kỳ chương trình nào thay mặt người dùng (sử dụng eg / bin / sh -c "lệnh"). Đây là một truyền thống Unix và có những lợi thế:

  • Thiết lập thông thường của người dùng (bí danh lệnh, biến môi trường, ô, v.v.) có hiệu lực khi các lệnh từ xa được chạy.
  • Thực tiễn phổ biến về việc đặt shell của tài khoản thành / bin / false để vô hiệu hóa nó sẽ ngăn chủ sở hữu chạy bất kỳ lệnh nào, vì vậy việc xác thực vẫn vô tình thành công vì một số lý do.

Chi tiết giao thức SCP

Đối với những người quan tâm đến các chi tiết về cách thức hoạt động của SCP, tôi đã tìm thấy thông tin thú vị trong cách thức giao thức SCP hoạt động bao gồm các chi tiết về Chạy scp với các cấu hình vỏ nói chuyện ở phía xa? :

Ví dụ, điều này có thể xảy ra nếu bạn thêm nó vào hồ sơ shell của mình trên hệ thống từ xa:

tiếng vang ""

Tại sao nó chỉ treo? Điều đó xuất phát từ cách thức scptrong chế độ nguồn chờ xác nhận thông báo giao thức đầu tiên. Nếu nó không phải là nhị phân 0, nó hy vọng rằng đó là thông báo về sự cố từ xa và chờ thêm ký tự để tạo thông báo lỗi cho đến khi dòng mới xuất hiện. Vì bạn không in một dòng mới sau dòng đầu tiên, địa phương của bạn scpchỉ ở trong một vòng lặp, bị chặn read(2). Trong khi đó, sau khi cấu hình shell được xử lý ở phía xa, scpở chế độ chìm được bắt đầu, nó cũng bị chặn read(2), chờ số 0 nhị phân biểu thị bắt đầu truyền dữ liệu.

Kết luận / TLDR

Hầu hết các câu lệnh trong một điển hình .bashrcchỉ hữu ích cho một vỏ tương tác - không phải khi chạy các lệnh từ xa với rshhoặc ssh. Trong hầu hết các tình huống như vậy, việc đặt các biến shell, bí danh và các hàm xác định là không mong muốn - và việc in bất kỳ văn bản nào thành tiêu chuẩn sẽ gây hại tích cực nếu chuyển các tệp bằng các chương trình như scphoặc sftp. Thoát ra sau khi xác minh rằng trình bao hiện tại không tương tác là hành vi an toàn nhất cho .bashrc.


bài viết hay Nhưng tôi nên làm gì với điều đó? Tôi đã đăng ký bằng .bashrc nhưng vẫn thoát scp bằng 0 mã và không sao chép bất cứ thứ gì vào hộp từ xa
Ses

@ vòi Tốt nhất là hỏi một câu hỏi mới, nơi bạn có thể mô tả tình huống của bạn một cách chi tiết hơn.
Anthony G - công lý cho Monica

16

Trang người đàn ông bỏ qua đề cập đến đó bashcũng là nguồn .bashrccho các shell từ xa không tương tác, như trong

ssh hostname command

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1010

 COMMAND        EXECUTE BASHRC
 --------------------------------
 bash -c foo        NO
 bash foo           NO
 foo                NO
 rsh machine ls     YES (for rsh, which calls 'bash -c')
 rsh machine foo    YES (for shell started by rsh) NO (for foo!)
 echo ls | bash     NO
 login              NO
 bash               YES

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1050

          /* If we were run by sshd or we think we were run by rshd, execute
             ~/.bashrc if we are a top-level shell. */
          if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
            {
              maybe_execute_file (SYS_BASHRC, 1);
              maybe_execute_file (bashrc_file, 1);

Bạn có nguồn .bashrc từ .bash_profile (hoặc .profile) của bạn không?
glenn jackman

Vâng, nhưng đó không phải là vấn đề. Một sản phẩm nào .bash_profilesẽ thể hiện hành vi tương tự.
Mikel

Không chắc chắn những gì cho tôi thấy. Bạn đang nói rằng rsh(mà tôi chưa bao giờ sử dụng) nguồn ~/.bashrc? Và rằng, bằng cách mở rộng, Linux distro chặn nó bashchỉ trong trường hợp bất cứ ai từng cố gắng chạy một kịch bản với rsh? ssh serverkhông nên chạm vào .bashrcvì đó là vỏ đăng nhập. Không, trừ khi hệ thống của bạn là một trong những nguồn ~ / .bashrc` từ ~/.profile. Và ssh server commandthậm chí không nên làm điều đó. Chắc chắn không có trên hệ thống của tôi.
terdon

2
Không. Tôi liên kết bạn với bashnguồn chứ không phải rshnguồn. :) Nhận xét cũng được áp dụng ssh, nó chỉ được viết cách đây rất lâu. Xem câu trả lời cập nhật với nhiều nguồn hơn.
Mikel

Ah, thật hữu ích, cảm ơn bạn. Làm thế nào tôi sẽ kiểm tra điều này mặc dù? Tôi đã thử thêm một cái echoở đầu /etc/bash.bashrc~/.bashrc(trước khi thử nghiệm vỏ tương tác) và sau đó ssh vào máy mục tiêu ssh server hostnamevà trong khi hostnamechạy, tôi không thấy tiếng vang của mình. Có phải là tôi shell_level(dù đó là gì) không <2?
terdon

5

Theo quy ước, .bashrclà nơi người dùng lưu trữ cấu hình tùy chỉnh cho shell.

Các cấu hình tùy chỉnh này có thể là các biến môi trường, bí danh, dấu nhắc ưa thích. Với lớp vỏ không tương tác, những thứ ngắn ngủi đó là vô nghĩa. Hơn nữa, một vỏ không tương tác có thể được gọi trong nhiều ngữ cảnh, bạn không chắc chắn các biến môi trường đó có thể dẫn đến trường hợp phủ định sai hoặc thậm chí dễ bị tổn thương về bảo mật.

Một ví dụ gần nhất là một bí danh như:

alias cp='cp -i'

Sau đó, nó treo vỏ không tương tác của bạn mãi mãi.

Vì vậy, kiểm tra thực hiện ở đầu .bashrcđể đảm bảo rằng chúng tôi sẽ không gặp rắc rối.


Bởi vì shell có thể được gọi là shell đăng nhập không tương tác , do đó, việc chặn nguồn rõ ràng *bashrckhông có ý nghĩa gì.

Khi vỏ được gọi là vỏ đăng nhập không tương tác , nó nguồn /etc/profile, sau đó nguồn là người đầu tiên tìm thấy trong ~/.bash_profile, ~/.bash_login~/.profile:

Khi bash được gọi dưới dạng shell đăng nhập tương tác hoặc dưới dạng shell không tương tác với tùy chọn --login , trước tiên, nó sẽ đọc và thực thi các lệnh từ tệp / etc / profile, nếu tệp đó tồn tại. Sau khi đọc tệp đó, nó tìm ~ / .bash_profile, ~ / .bash_login và ~ / .profile, theo thứ tự đó, đọc và thực thi các lệnh từ lệnh đầu tiên tồn tại và có thể đọc được.

Không có gì ngăn cản các tập tin .bashrcđó tự tìm nguồn cung ứng , vì vậy thực hiện kiểm tra bên trong .bashrcsẽ an toàn hơn và làm cho mọi thứ trở nên đơn giản.


Vâng tôi biết điều đó. Đây là lý do tại sao các vỏ không tương tác hoàn toàn không có *bashrctệp nguồn . Câu hỏi của tôi là tại sao các nhà cung cấp Linux khác nhau gặp rắc rối trong việc ngăn chặn rõ ràng các shell không tương tác đọc các tệp này nếu các tệp này không được đọc bởi các shell không tương tác.
terdon

1
@terdon: Bởi vì shell có thể được gọi là shell đăng nhập không tương tác , nên shell đăng nhập sẽ nguồn .bash_profile, có thể là nguồn .bashrc.
cuonglm

Không, trong các vỏ không tương tác, điều duy nhất được đọc là $BASH_ENV. Cả hai *profile*bashrcđược bỏ qua. Hoặc, ít nhất, đó là những gì trang đàn ông nói. Tuy nhiên, như Mikel đã cho thấy trang người đàn ông có thể đang nói dối.
terdon

@terdon: Đọc câu trả lời cập nhật của tôi với liên kết trong đó. Bạn đã thử bash -l -c :gọi một vỏ đăng nhập không tương tác?
cuonglm

Ồ Bạn hoàn toàn đúng. Tôi đã đọc phần đó khoảng --login100 lần nhưng rõ ràng, nó chưa bao giờ đăng ký. Cảm ơn!
terdon
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.