Làm thế nào một tập lệnh Bash có thể cho biết nó đã được chạy như thế nào?


10

Tôi có một đoạn script Bash mà tôi đang cố gắng thực hiện để giúp tôi chạy một lệnh khá phức tạp với những thay đổi nhỏ mà nó sẽ hỏi tôi về tiếng vang và đọc.

Tôi đã tìm thấy các giải pháp để buộc nó chạy một thiết bị đầu cuối để thực thi lệnh, nhưng tôi không quan tâm đến điều đó. Những gì tôi muốn nó làm là, nếu tôi thoát ra và chỉ cần nhấn Enter trên nó trong Nautilus (làm cho nó chạy với Run Software), nó sẽ chỉ nhẹ nhàng bật lên một thông báo có nội dung "Vui lòng chạy cái này từ thiết bị đầu cuối."

Tôi có thể khiến cửa sổ bật lên xảy ra - như tôi biết lệnh - nhưng tôi không thể lấy tập lệnh Bash để biết liệu nó có được chạy bên trong một thiết bị đầu cuối hay không, dường như nó luôn nghĩ như vậy. Nó thậm chí có thể?

Câu trả lời:


10

Từ man bashdưới GIẢI THÍCH ĐIỀU KIỆN :

-t fd  
    True if file descriptor fd is open and refers to a terminal.

Giả sử fd 1 là tiêu chuẩn, if [ -t 1 ]; thennên làm việc cho bạn. Các chi tiết Shell Scripting Guide tuyên bố rằng -tsử dụng theo cách này sẽ thất bại trên ssh, và rằng các thử nghiệm (sử dụng stdin, stdout không) nên do đó là:

if [[ -t 0 || -p /dev/stdin ]]

-pkiểm tra nếu một tập tin tồn tại và là một ống có tên. Tuy nhiên , tôi lưu ý về mặt kinh nghiệm, điều này không đúng với tôi: -p /dev/stdinthất bại cho cả thiết bị đầu cuối và phiên ssh bình thường trong khi if [ -t 0 ](hoặc -t 1) hoạt động trong cả hai trường hợp (xem thêm các bình luận của Gilles bên dưới về các vấn đề trong phần đó của Hướng dẫn kịch bản Shell nâng cao ).


Nếu vấn đề chính là bối cảnh chuyên biệt mà bạn muốn gọi tập lệnh để xử lý theo cách phù hợp với bối cảnh đó, bạn có thể bỏ qua tất cả các kỹ thuật này và tự cứu mình bằng cách sử dụng trình bao bọc và biến tùy chỉnh:

!#/bin/bash

export SPECIAL_CONTEXT=1
/path/to/real/script.sh

Gọi đây live_script.shhoặc bất cứ điều gì và nhấp đúp vào đó thay vào đó. Tất nhiên bạn có thể hoàn thành điều tương tự với các đối số dòng lệnh, nhưng vẫn cần một trình bao bọc để tạo điểm và nhấp trong trình duyệt tệp GUI.


5
đây là câu trả lời đúng - đó cũng là cách POSIX nói một trình bao sẽ phát hiện xem nó có tương tác hay không.
mikeerv

2
@DanielAmaya - nếu bạn chuyển hướng đầu vào thì tập lệnh sẽ không được chạy trên thiết bị đầu cuối. Câu hỏi là làm thế nào để phát hiện nếu tập lệnh đang được chạy trên một thiết bị đầu cuối.
mikeerv

2
Bạn có chắc chắn về việc sử dụng ||bên trong [ … ]như vậy? Nếu bạn sử dụng [[ … ]]thì nó sẽ ổn, nhưng thông thường, ||nó được sử dụng để phân tách các lệnh và [ -t 0là một cách gọi không chính xác [vì cuối cùng của nó ]bị thiếu. Thông thường không phải là một lệnh -p. Tôi đồng ý với thử nghiệm cho một thiết bị đầu cuối; đó có lẽ là cách để làm điều đó Đó chỉ là cú pháp tôi quan tâm.
Jonathan Leffler

1
@JonathanLeffler Phải; điều đó sẽ tạo ra một lỗi cú pháp, vì toán tử shell ||được nhìn thấy trước ]đối số cuối cùng được yêu cầu [.
chepner

3
Phần đó từ Hướng dẫn Bash-Scripting nâng cao có một số lỗi. PS1không phải là một thử nghiệm đáng tin cậy để biết liệu vỏ có tương tác hay không. Nếu một kịch bản cần kiểm tra xem nó có đang chạy trong một vỏ tương tác hay không thì cũng khó hiểu: đó là nếu một số mã cần kiểm tra - một kịch bản thường không chạy trong một vỏ tương tác (nhưng có thể, nếu nó có nguồn gốc) . Kiểm tra itrong $-là cách chính xác để kiểm tra nếu shell tương tác. Kiểm tra -t 0hoặc -t 2là cách chính xác để biết liệu tập lệnh có đang chạy trong một thiết bị đầu cuối hay không, khác với việc tương tác.
Gilles 'SO- ngừng trở nên xấu xa'

0

Sử dụng biến bash $ SHLVL để phát hiện mức độ lồng nhau của vỏ. Trong một tập lệnh chạy 'thô' bằng cách bấm đúp vào nó sẽ là 1, trong một tập lệnh chạy trong một thiết bị đầu cuối, nó sẽ là 2.

#!/bin/bash
if (( SHLVL < 2 )) ; then
    echo "Please run this from a terminal."
    read -p "Press <Enter> to close this window"
    exit 1
fi
# rest of script

0

Mặc dù câu trả lời của goldilocks có lẽ đúng trong trường hợp điển hình, nhưng dường như có trường hợp cạnh. Trong trường hợp của riêng tôi, xserver của tôi được cấu hình để khởi động tty1và nó không bao giờ rời khỏi đó. Nếu Xorg stdoutlà một TTY thì có vẻ như các máy khách sẽ có TTY đó được liên kết với bộ mô tả tệp theo mặc định.

Đây là cách tôi giải quyết vấn đề của mình:

#!/bin/bash
isxclient=$( readlink /dev/fd/2 | grep -q 'tty' && [[ -n $DISPLAY ]] ; echo $? )
if [[ ! -t 2  || $isxclient == "0" ]]; then
        notify-send "Script wasn't started from an interactive shell"
else
        echo "Script was started from an interactive shell"
fi

Tôi đã không thử nghiệm điều này để xem nó có hoạt động trên cấu hình X tiêu chuẩn hơn không và tôi cũng nghi ngờ rất nhiều rằng đây là trường hợp cạnh duy nhất. Nếu bất cứ ai tìm thấy một giải pháp thường áp dụng hơn, xin vui lòng quay lại và nói với chúng tôi.


-2

Khác, sử dụng các tùy chọn bash đặt biến nội bộ , $-.

Từ .bashrc,

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

một vỏ tương tác không nhất thiết phải được kết nối với một thiết bị đầu cuối. trong khi một bắt đầu với kết nối đó được tự động bắt đầu tương tác, điều này cũng có thể : cmd | sh -i | cmd.
mikeerv

Mã này đang được thực thi trong một kịch bản. Nó sẽ không tương tác, ngay cả khi nó đang chạy trong một thiết bị đầu cuối.
Gilles 'SO- ngừng trở nên xấu xa'
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.