Làm cách nào tôi có thể ngăn các tùy chọn 'shopt' không được hỗ trợ gây ra lỗi trong .bashrc của mình?


9

Tôi làm việc trong một môi trường tương đối không đồng nhất, nơi tôi có thể đang chạy các phiên bản Bash khác nhau trên các nút HPC, VM hoặc máy trạm cá nhân khác. Bởi vì tôi đặt các tập lệnh đăng nhập của mình trong một repo Git, tôi muốn sử dụng cùng (ish) .bashrctrên bảng, mà không có nhiều "nếu máy chủ này, thì ..." - gõ lộn xộn.

Tôi giống như hành vi mặc định của Bash ≤ 4.1 mà mở rộng cd $SOMEPATHvào cd /the/actual/pathkhi nhấn Tabphím. Trong Bash 4.2 trở lên, bạn sẽ cần phải shopt -s direxpandkích hoạt lại hành vi này và điều đó không khả dụng cho đến 4.2,29 . Đây chỉ là một ví dụ; một shopttùy chọn khác, có thể liên quan , complete_fullquote(mặc dù tôi không biết chính xác nó làm gì) cũng có thể đã thay đổi hành vi mặc định tại v4.2.

Tuy nhiên, direxpandkhông được công nhận bởi các phiên bản trước đó của Bash, và nếu tôi cố gắng shopt -s direxpandtrong tôi .bashrc, mà kết quả trong một thông báo lỗi được in ra cửa sổ Console mỗi khi tôi đăng nhập vào một nút với một Bash trở lên:

-bash: shopt: direxpand: invalid shell option name

Những gì tôi muốn làm là bao bọc một điều kiện xung quanh shop -s direxpandđể cho phép tùy chọn đó trên Bash> 4.1 một cách mạnh mẽ, mà không làm hỏng các phiên bản cũ hơn của Bash ( nghĩa là không chỉ chuyển hướng đầu ra lỗi sang /dev/null).


Làm thế nào câu trả lời của tôi không giúp đỡ?
Luciano Andress Martini

@LucianoAndressMartini Nó đã làm, và đó là giải pháp tôi tự mình thực hiện .bashrc. Tôi vẫn muốn có một bản ghi về cách sử dụng $BASH_VERSINFOđể thẩm vấn phiên bản chính / phụ của shell đang chạy, vì sự chỉnh sửa của riêng tôi, đó là lý do tại sao tôi hoàn thành việc đăng câu trả lời của riêng mình. :)
TheDudeAdides

Nhìn vào câu trả lời của tôi, tôi có vài thứ về việc so sánh phiên bản chương trình với shell script.
Luciano Andress Martini

Câu trả lời:


14

Kiểm tra nếu direxpandcó trong đầu ra của shoptvà kích hoạt nó nếu nó là:

shopt | grep -q '^direxpand\b' && shopt -s direxpand

4
Tốt hơn là làm grep -q '^direxpand\b'trong trường hợp một số phiên bản hoặc fork của bash trong tương lai có một tùy chọn chứa chuỗi này dưới dạng chuỗi con loại bỏ direxpand. Không có khả năng trong trường hợp cụ thể này, nhưng nó không tốn nhiều chi phí để trở nên mạnh mẽ.
Gilles 'SO- ngừng trở nên xấu xa'

Cảm ơn Luciano. Tôi dự định trả lời câu hỏi của riêng tôi, nhưng tôi sẽ chấp nhận câu trả lời của bạn sau khi các chỉnh sửa của tôi được duyệt qua đánh giá ngang hàng. Có lẽ bạn có thể tự mình chấp nhận chúng không?
TheDude Abides 5/2/19

4
Bash cho phép truy vấn các tùy chọn shell cụ thể, vì vậy người ta có thể sử dụng [ -z "$(shopt -po direxpand 2>&-)" ] || shopt -s direxpand. Không có nhiều vấn đề regex! :-)
David Foerster

@DavidFoerster Tôi sẽ chuyển logic: [ -n "blah" ] && shopt blahCách bạn diễn đạt nó, bạn đang nói "nếu direxpand không được hỗ trợ, thì đừng làm điều này".
Giàu

1
@Rich: Hầu hết các tập lệnh shell của tôi bao gồm set -eở trên cùng, vì vậy tôi có xu hướng sử dụng logic cắt ngắn theo cách này.
David Foerster

16

Tôi không thấy có gì sai khi chuyển hướng lỗi sang /dev/null. Nếu bạn muốn mã của mình mạnh mẽ set -e, hãy sử dụng thành ngữ phổ biến … || true:

shopt -s direxpand 2>/dev/null || true

Nếu bạn muốn chạy một số mã dự phòng nếu tùy chọn không tồn tại, hãy sử dụng trạng thái trả về của shopt:

if shopt -s direxpand 2>/dev/null; then
   # the direxpand option exists
else
   # the direxpand option does not exist
fi

Nhưng nếu bạn thực sự không thích chuyển hướng lỗi đi, bạn có thể sử dụng cơ chế hoàn thành để thực hiện nội quan. Điều này giả định rằng bạn không có máy cũ với bash ≤ 2.03 chưa hoàn thành lập trình.

shopt_exists () {
  compgen -A shopt -X \!"$1" "$1" >/dev/null
}
if shopt_exists direxpand; then
  shopt -s direxpand
fi

Phương pháp này tránh được việc rèn, chậm trên một số môi trường như Cygwin. 2>/dev/nullNói một cách đơn giản , tôi không nghĩ bạn có thể đánh bại điều đó về hiệu suất.


Đó không phải là nơi não tôi sẽ đi, nhưng tôi thích compgenđề xuất này. Đó là thứ varsity cấp ngay đó! Tránh chuyển hướng đến /dev/nullchỉ là một sở thích cá nhân. Tôi thích xin phép thay vì tha thứ, nếu điều đó có ý nghĩa? :)
TheDudeAdides

+1 cho một trường học hoàn toàn không dự đoán trước khi hoàn thành lập trình Bash, điều đó buộc tôi phải đi đến hướng dẫn để giải mã những gì compgen -A shopt -X ...thậm chí có nghĩa.
TheDudeAdides

4
@TheDudeAdides Tôi đọc về cách sử dụng cách compgennày trên Unix & Linux , tôi không biết ai là người đầu tiên đề xuất nó. . lỗi (trong đó bạn không hoàn toàn kiểm tra những gì bạn nghĩ bạn đang kiểm tra) hoặc vì những gì bạn đã kiểm tra đã thay đổi trước khi bạn sử dụng nó .
Gilles 'SO- ngừng trở nên xấu xa'

5

Khi bạn biết chắc chắn rằng một shopttùy chọn cụ thể có sẵn tại một bản phát hành chính / phụ / bản vá nhất định của Bash, bạn có thể kiểm tra $BASH_VERSIONbiến hoặc các phần tử của $BASH_VERSINFO[]mảng để kích hoạt nó một cách có điều kiện.

Đây là bản thử nghiệm cho Bash 4.2,29 trở lên, phiên bản direxpand được giới thiệu lần đầu tiên cho loạt 4.2:

if [[ $BASH_VERSION == 4.2.* && ${BASH_VERSINFO[2]} -ge 29 ]] ||
   [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -ge 3 ]] ||
   [[ ${BASH_VERSINFO[0]} -ge 5 ]]; then
    shopt -s direxpand
fi

Chỉnh sửa: Để rõ ràng, đây là một giải pháp được thiết kế quá mức lố bịch vì chỉ cần bỏ qua một thông báo lỗi đến từ các tập lệnh đăng nhập của bạn, nhưng tôi đã muốn ghi lại nó bất kể, cho chỉnh sửa của riêng tôi.

Lưu ý các dấu ngoặc xung quanh , được yêu cầu, và việc sử dụng và , làm số nguyên thay vì so sánh từ vựng (phụ thuộc cục bộ ). Nếu không được trích dẫn, RHS của toán tử được coi là các mẫu "extglob" trong Bash / điều kiện, như đã lưu ý ở đây , điều này làm cho một "so sánh" thẩm mỹ hơn so với một biểu thức chính quy, IMO.${BASH_VERSINFO[index]}-eq-gt==[[]]

Các $BASH_VERSINFOmảng chứa tất cả các thông tin mà bạn muốn thấy trong đầu ra của bash --version:

bash --version | head -1
# result:
# GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

declare -p BASH_VERSINFO
# result:
# declare -ar BASH_VERSINFO='([0]="4" [1]="3" [2]="48" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")'

Khi không rõ tài liệu về việc shoptphiên bản Bash được hỗ trợ hoặc thay đổi hành vi của họ, phương pháp do Luciano đề xuất là ổn:

# note the '-q' so that the matched pattern isn't actually printed
shopt | grep -q direxpand && shopt -s direxpand

... như là giải pháp được đề xuất bởi Gilles chỉ cần bỏ qua lỗi ( shopt -s direxpand 2>/dev/null) và có thể kiểm tra $?nếu thực sự cần thiết.

Tài liệu tham khảo: 1 , 2 , 3
Đọc liên quan: Đặt và Shopt - Tại sao lại là hai?


Bạn cũng có thể sử dụng một cái gì đó như if [[ $BASH_VERSION > 4.3 ]];(khớp 4.3.0, 5.0v.v., nhưng 4.3.0-alphatôi cũng không biết liệu thực tế sau này có quan trọng không.)
ilkkachu

Xin chào @ilkkachu. Cảm ơn bạn đã chỉnh sửa để bao quát Bash v5.x. Các direxpandlựa chọn là thực sự sẵn sàng cho Bash 4.2, mặc dù; Tôi đã xác minh điều này bằng hình ảnh Docker tại v4.2.53 bằng cách chạy docker run --rm bash:4.2 bash -c shopt | grep direxpand(và, đối với biện pháp tốt, nó thực sự không có sẵn tại v4.1.17 bằng cách chạy docker run --rm bash:4.1 bash -c shopt | grep direxpand).
TheDudeAdides

ah ok, tôi đã thử nghiệm 4.2.0và tình cờ thấy nó không hoạt động ở đó. Các thay đổi cũng đề cập đến nó được thêm vào bash-4.3-alpha. Tôi cho rằng sau đó người ta sẽ cần phải kiểm tra ${BASH_VERSINFO[2]}chính xác về nó, nhưng tôi không biết bản phát hành điểm nào đã thêm nó ...
ilkkachu

Tôi nghĩ rằng về cơ bản chúng tôi đã chứng minh điểm Gilles đã làm ở trên; trên thực tế, tốt hơn là chỉ nên thử bật tùy chọn shell và sau đó xử lý lỗi (hoặc loại bỏ nó) nếu nó không được hỗ trợ.
TheDudeAdides
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.