bash script: kết quả khác nhau khi được gọi có hoặc không có sudo


10

Trong Ubuntu 16.04.3, tôi có một tập lệnh bash rất đơn giản:

kiểm tra

[[ 0 == 0 ]] && result="true" || result="false"
echo $result
echo $USER $SHELL $0

Khi tôi gọi nó là người dùng không root mehoặc as root, nó hoạt động như mong đợi. Nếu tôi sử dụng sudo ./test.sh, nó sẽ phàn nàn về lỗi cú pháp:

$ ./test.sh
true
me /bin/bash ./test.sh

$ sudo su
# ./test.sh 
true
root /bin/bash ./test.sh

# exit
$ sudo ./test.sh
./test.sh: 1: ./test.sh: [[: not found
false
root /bin/bash ./test.sh

Điều gì có thể gây ra điều này? Làm thế nào tôi có thể sửa nó để mecó thể sử dụng tập lệnh này cả bình thường và với sudo?


3
Mẹo chuyên nghiệp: không có điểm nào trong việc chạysudo su . Chỉ cần chạy sudo -ihoặc sudo -sthay vào đó.
terdon

@terdon sudo -ithay đổi vị trí thành /root. sudo suhoặc sudo -skhông thay đổi vị trí thư mục.
James Newton

Vâng, đọc câu hỏi tôi liên kết trước đó tại sao. Và xin lỗi, tôi đã chỉnh sửa bình luận trước đây của tôi, tôi đã quên đề cập đến -s.
terdon

Câu trả lời:


20

Mọi tập lệnh bắt đầu bằng một Shebang , không có trình bao, tập lệnh bắt đầu tập lệnh của bạn không biết trình thông dịch nào sẽ chạy tập lệnh của bạn 1 và có thể - như trong trường hợp sudo ./script.shở đây - chạy nó với sh, trong Ubuntu 16.04 được liên kết với dash. Các biểu thức điều kiện [[ là một bashlệnh phức hợp , vì vậy dashkhông biết làm thế nào để xử lý nó và ném lỗi mà bạn gặp phải.

Giải pháp ở đây là thêm

#!/bin/bash

là dòng đầu tiên của kịch bản của bạn. Bạn có thể nhận được kết quả tương tự khi bạn gọi nó một cách rõ ràng sudo bash ./script.sh, nhưng một shebang là cách để đi.
Để kiểm tra shell nào chạy script của bạn, hãy thêm echo $0nó vào. Điều đó không giống như echo $SHELL , trích dẫn wiki.archlinux.org :

SHELL chứa đường dẫn đến vỏ ưa thích của người dùng. Lưu ý rằng đây không nhất thiết là trình bao hiện đang chạy, mặc dù Bash đặt biến này khi khởi động.

1: Khi bạn bắt đầu ./test.shvới bashnó chỉ là giả định bash, điều tương tự cũng xảy ra với sudo susubshell.


1
Cũng lưu ý rằng bash chạy tập lệnh mà không có shebang sử dụng bash, không /bin/sh.
muru

@datcher Điều đó sửa nó. Cảm ơn! Làm thế nào tôi có thể kiểm tra từ bên trong một tập lệnh mà shell đang chạy nó? ( echo $0cho tôi tên của kịch bản ./test.sh:)
James Newton

@JamesNewton không có cách di động, AFAIK, nhưng bạn có thể kiểm tra những /proc/$$/exeđiểm nào. Ngoài ra, bạn có thể kiểm tra các biến khác nhau như $BASH_VERSION, $ZSH_VERSIONv.v. (nhưng dấu gạch ngang không đặt bất kỳ biến nào như vậy)
muru

5

Như @dPlay đã giải thích , vấn đề ở đây là tập lệnh của bạn không có dòng shebang . Nếu không có shebang, sudosẽ mặc định chạy tệp bằng cách sử dụng /bin/sh. Tôi không thể tìm thấy tài liệu ở bất cứ đâu, nhưng tôi đã xác nhận bằng cách kiểm tra sudomã nguồn nơi tôi tìm thấy sau đây trong tệp pathnames.h:

#ifndef _PATH_BSHELL
#define _PATH_BSHELL "/bin/sh"
#endif /* _PATH_BSHELL */

Điều này có nghĩa là "đặt nếu biến _PATH_BSHELLkhông được xác định, đặt thành /bin/sh". Sau đó, trong configuretập lệnh có trong tarball nguồn, chúng ta có:

for p in "/bin/bash" "/usr/bin/sh" "/sbin/sh" "/usr/sbin/sh" "/bin/ksh" "/usr/bin/ksh" "/bin/bash" "/usr/bin/bash"; do
    if test -f "$p"; then
    found=yes
    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $p" >&5
$as_echo "$p" >&6; }
    cat >>confdefs.h <<EOF
#define _PATH_BSHELL "$p"
EOF

    break
    fi
done

Vòng lặp này sẽ tìm kiếm /bin/bash, /usr/bin/sh, /sbin/sh, /usr/sbin/shhay /bin/kshvà sau đó thiết lập _PATH_BSHELLđể bất cứ đã được tìm thấy đầu tiên . Vì /bin/shlà cái đầu tiên trong danh sách và nó tồn tại, _PATH_BSHELLđược đặt thành /bin/sh. Kết quả của tất cả điều này là lớp vỏ mặc định sudotrừ khi có quy định khác /bin/sh.

Vì vậy, sudosẽ mặc định chạy mọi thứ bằng cách sử dụng /bin/shvà trên Ubuntu, đó là một liên kết tượng trưng đến dash, trình bao tương thích POSIX tối thiểu:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Feb 27  2015 /bin/sh -> dash

Cấu [[trúc là một tính năng bash, nó không được xác định bởi tiêu chuẩn POSIX và không được hiểu bởi dash:

$ bash -c '[[ true ]] && echo yes'
yes
$ dash -c '[[ true ]] && echo yes'
dash: 1: [[: not found

Cụ thể, trong ba lời mời bạn đã thử:

  1. ./test.sh

    Không sudo; trong trường hợp không có dòng shebang, shell của bạn sẽ cố gắng tự thực thi tệp. Vì bạn đang chạy bash, điều này sẽ chạy bash ./test.shvà làm việc hiệu quả .

  2. sudo sutheo sau ./test.sh.

    Tại đây, bạn đang bắt đầu một trình bao mới cho người dùng root. Đây sẽ là bất kỳ shell nào được xác định trong $SHELLbiến môi trường cho người dùng đó và trên Ubuntu, shell mặc định của root là bash:

    $ grep root /etc/passwd
    root:x:0:0:root:/root:/bin/bash
    
  3. sudo ./test.sh

    Ở đây, bạn đang cho phép sudothực hiện lệnh trực tiếp. Vì lớp vỏ mặc định của nó /bin/shnhư được giải thích ở trên, điều này khiến nó chạy tập lệnh với /bin/sh, dashvà nó không thành công do dashkhông hiểu [[.


Lưu ý : các chi tiết về cách sudođặt shell mặc định có vẻ phức tạp hơn một chút. Tôi đã thử thay đổi các tệp được đề cập trong câu trả lời của mình để trỏ đến /bin/bashnhưng sudovẫn được mặc định /bin/sh. Vì vậy, phải có một số vị trí khác trong mã nguồn nơi vỏ mặc định được xác định. Tuy nhiên, điểm chính ( sudomặc định là sh) vẫn đứng.


Tôi không thể tìm thấy nó được ghi lại ở bất cứ đâu - tôi cũng vậy, tôi chỉ cho rằng nó sẽ sử dụng /bin/shtừ thông báo lỗi - nó có thể là gì khác? Câu hỏi được trả lời rất hay trong phần sudo nào sử dụng · SO , xem thêm man sudo, phần THỰC HÀNH . Hóa ra sudokhông sử dụng vỏ trung gian !
tráng miệng

1
@dPlay có, nó sử dụng việc thực hiện execvecuộc gọi hệ thống của riêng mình mà mặc định sh. Và không, các shell trung gian là không liên quan, đây không phải là về shell chạy lệnh mà là về trình thông dịch shell được sử dụng để đọc tập lệnh shell được đưa ra. Vì vậy, nó không khởi chạy shell trung gian nhưng nó vẫn cần một trình thông dịch shell cho các kịch bản shell.
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.