cách phù hợp với checkbashism để xác định shell hiện tại


18

Theo tôi .profile, tôi sử dụng đoạn mã sau để đảm bảo rằng các bí danh và hàm liên quan đến Bash chỉ có nguồn gốc nếu vỏ đăng nhập thực sự là Bash :

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

Tôi hiện đang trong quá trình đặt các tệp cấu hình shell, tập lệnh và chức năng của mình dưới sự kiểm soát phiên bản. Gần đây tôi cũng đã bắt đầu quá trình loại bỏ Bashism thông thường khỏi các tập lệnh shell không được hưởng lợi từ các tính năng dành riêng cho Bash, ví dụ: thay thế function funcname()bằng funcname().

Đối với kho lưu trữ file shell của tôi, tôi đã cấu hình một cam kết trước móc chạy các checkbashismstiện ích từ Debian của gói devscripts trên mỗi shtập tin trong kho để đảm bảo rằng tôi không vô tình giới thiệu cú pháp Bash cụ thể. Tuy nhiên, điều này gây ra lỗi cho tôi .profile:

possible bashism in .profile line 51 ($BASH_SOMETHING):
if [ "${BASH_VERSION:-}" ]; then

Tôi đã tự hỏi nếu có một cách để kiểm tra xem vỏ nào đang chạy sẽ không kích hoạt cảnh báo checkbashisms.

Tôi đã kiểm tra danh sách các biến liên quan đến shell được liệt kê bởi POSIX với hy vọng rằng một trong số chúng có thể được sử dụng để hiển thị shell hiện tại. Tôi cũng đã xem xét các biến được đặt trong trình bao Dash tương tác, nhưng, một lần nữa, không tìm được ứng cử viên phù hợp.

Hiện tại, tôi đã loại trừ .profilekhỏi bị xử lý bởi checkbashisms; đó là một tập tin nhỏ nên không khó để kiểm tra thủ công. Tuy nhiên, khi nghiên cứu vấn đề này, tôi vẫn muốn biết liệu có phương pháp tuân thủ POSIX nào để xác định lớp vỏ nào đang chạy hay không (hoặc ít nhất là một cách không gây ra checkbashismslỗi).


Thông tin cơ bản / làm rõ

Một trong những lý do khiến tôi đặt các tệp cấu hình shell của mình dưới sự kiểm soát phiên bản là để định cấu hình môi trường của tôi trên tất cả các hệ thống mà tôi hiện đang đăng nhập thường xuyên: Cygwin, Ubuntu và CentOS (cả 5 và 7, sử dụng Active Directory cho người dùng xác thực). Tôi thường xuyên đăng nhập thông qua X Windows / môi trường máy tính để bàn và SSH cho các máy chủ từ xa. Tuy nhiên, tôi muốn đây là bằng chứng trong tương lai và có ít sự phụ thuộc nhất vào hệ thống phụ thuộc và các công cụ khác nhất có thể.

Tôi đã sử dụng checkbashismsnhư một kiểm tra độ tỉnh táo tự động đơn giản cho cú pháp của các tệp liên quan đến vỏ của tôi. Nó không phải là một công cụ hoàn hảo, ví dụ, tôi đã áp dụng một bản vá cho nó để nó không phàn nàn về việc sử dụng các command -vtập lệnh của tôi. Trong khi nghiên cứu, tôi đã học được rằng mục đích thực tế của chương trình là đảm bảo tuân thủ chính sách Debian, theo tôi hiểu, dựa trên POSIX 2004 chứ không phải 2008 (hoặc sửa đổi năm 2013).


Những gì bạn đang làm là tuân thủ POSIX như mọi thứ có thể là khi toàn bộ vấn đề là chạy khác nhau trên các môi trường tuân thủ POSIX khác nhau. Thịt bò của bạn là với checkbashism.
Gilles 'SO- ngừng trở nên xấu xa'

Và nhân tiện, tôi chưa bao giờ sử dụng checkbashism để kiểm tra tính di động của cấu hình. Tôi kiểm tra nó bằng cách sử dụng nó trên các hệ thống khác nhau.
Gilles 'SO- ngừng trở nên xấu xa'

2
Và nhân tiện, đối với những việc cụ thể bạn đang làm ở đây, hãy viết một .bash_profilenguồn đó cả .profilevà (có điều kiện) .bashrc.
Gilles 'SO- ngừng trở nên xấu xa'

Cảm ơn phản hồi @Gilles. Đó là một giải pháp rất thanh lịch cho vấn đề cụ thể.
Anthony G - công lý cho Monica

Câu trả lời:


16

Của bạn

# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
  # source ~/.bashrc if it exists.
  if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
  fi
fi

mã hoàn toàn tuân thủ POSIX và cách tốt nhất để kiểm tra xem bạn có đang chạy không bash. Tất nhiên $BASH_VERSIONbiến là đặc thù của bash, nhưng đó là lý do tại sao bạn sử dụng nó! Để kiểm tra xem bạn có đang chạy không bash!

Lưu ý rằng $BASH_VERSIONsẽ được đặt cho dù bashđược gọi là bashhay sh. Khi bạn đã xác nhận rằng bạn đang chạy bash, bạn có thể sử dụng [ -o posix ]làm chỉ báo cho trình bao được gọi là sh(mặc dù tùy chọn đó cũng được đặt khi POSIXLY_CORRECT ở trong môi trường hoặc bashđược gọi với -o posix, hoặc với SHELLOPTS = posix trong môi trường. Nhưng trong tất cả các trường hợp đó, bashsẽ hành xử như thể được gọi là sh).


Một biến khác bạn có thể sử dụng thay vì $BASH_VERSIONvà điều checkbashismđó dường như không phàn nàn trừ khi thông qua -xtùy chọn là $BASH. Điều đó cũng cụ thể để bashbạn cũng có thể sử dụng để xác định xem bạn có đang chạy bashhay không.


Tôi cũng cho rằng nó không thực sự là một cách sử dụng đúng đắn checkbashisms. checkbashismslà một công cụ giúp bạn viết các shtập lệnh di động (theo shthông số kỹ thuật trong chính sách Debian, siêu bộ của POSIX), nó giúp xác định cú pháp không chuẩn được giới thiệu bởi những người viết tập lệnh trên các hệ thống có shliên kết tượng trưng bash.

A .profileđược giải thích bởi các shell khác nhau, nhiều trong số đó không tuân thủ POSIX. Nói chung, bạn không sử dụng shnhư vỏ đăng nhập của bạn, nhưng vỏ thích zsh, fishhoặc bashvới các tính năng tương tác tiên tiến hơn.

bashzsh, khi không được gọi là shvà khi tệp phiên hồ sơ tương ứng của họ ( .bash_profile, .zprofile) không tuân thủ POSIX (đặc biệt zsh) nhưng vẫn được đọc .profile.

Vì vậy, đó không phải là cú pháp POSIX mà bạn muốn .profilemà là một cú pháp tương thích với POSIX (cho sh) bashzshnếu bạn đã từng sử dụng các shell đó (thậm chí có thể là Bourne vì shell Bourne cũng đọc .profilenhưng không được tìm thấy trên các hệ thống dựa trên Linux ).

checkbashismschắc chắn sẽ giúp bạn tìm ra bashism nhưng có thể không chỉ ra cú pháp POSIX không tương thích với zshhoặc bash.

Ở đây, nếu bạn muốn sử dụng bashmã cụ thể (như công việc xung quanh bashlỗi đó, theo đó nó không đọc được ~/.bashrctrong các vỏ đăng nhập tương tác), một cách tiếp cận tốt hơn sẽ là ~/.bash_profilelàm điều đó (trước hoặc sau khi tìm nguồn cung ứng ~/.profilenơi bạn đặt phiên chung khởi tạo).


Điều đó không vượt qua checkbashismsvì biến được sử dụng.
Thomas Dickey

2
@ThomasDickey, vâng, nhưng đó là trường hợp báo cáo checkbashism nên được bỏ qua. Tất cả các giải pháp khác được đưa ra cho đến nay là tồi tệ hơn đáng kể.
Stéphane Chazelas

@ StéphaneChazelas Bạn nói rằng tất cả các giải pháp khác đều tồi tệ hơn. Điều gì sẽ là sai với việc sử dụng $0cho trường hợp cụ thể này?
Anthony G - công lý cho Monica

2
@AnthonyGeoghegan Điều đó không phân biệt giữa bash được gọi là shvà một số shell khác được gọi là sh.
Gilles 'SO- ngừng trở nên xấu xa'

Nó cũng cho kết quả dương tính giả nếu dashhoặc một vỏ không phải bash khác được gọi không chính xác argv[0] == "bash", điều này là hoàn toàn hợp pháp, nếu không thể.
Kevin

17

Thông thường một người sử dụng $0cho mục đích này. Trên trang web mà bạn liên kết, nó đứng:

0
   (Zero.) Expands to the name of the shell or shell script.

Quá dễ! Tôi đã quá quen với việc sử dụng $0tên của kịch bản shell, đến nỗi tôi quên mất rằng nó cũng có thể đề cập đến shell hiện tại. Cảm ơn!
Anthony G - công lý cho Monica

1
Như thường lệ, quy ước này được tôn trọng bởi tất cả các chương trình hoạt động tốt nhưng một chương trình độc hại có thể gây rắc rối cho bạn bằng cách sử dụng các cuộc gọi hệ thống khác nhau từ execgia đình vì chúng cho phép chương trình gọi xác định nhị phân được chạy tách biệt khỏi chuỗi được truyền làm đối số 0.
dmckee

Điều đó không hiệu quả với .profile :) dễ thương
Rob

5

Thông thường biến môi trường SHELL cho bạn biết shell mặc định. Bạn không cần phải tự tìm nguồn tệp .bashrc (không đúng khi xem bản cập nhật dưới đây), bash sẽ tự động làm điều này chỉ cần đảm bảo rằng nó nằm trong thư mục $ HOME.

Tùy chọn khác là làm một cái gì đó như:

 ps -o cmd= $$

sẽ cho bạn biết lệnh (không có đối số, = không có giá trị sẽ không hiển thị các tiêu đề cột) của quy trình hiện tại. Ví dụ đầu ra:

 $ps -o cmd= $$
 bash
 $sh
 $ps -o cmd= $$
 sh

CẬP NHẬT:

Tôi đứng sửa! :)

Các .bashrc không phải luôn luôn có nguồn gốc như đã được đề cập trong các ý kiến rống lên và /programming/415403/whats-the-difference-between-bashrc-bash-profile-and-environment

Vì vậy, bạn có thể di chuyển .bashrc của mình sang .bash_profile và xem nếu nó hoạt động mà không phải thực hiện kiểm tra. Nếu không bạn có bài kiểm tra trên.


1
.bashrckhông thực sự có nguồn gốc bởi một vỏ đăng nhập nhưng .profile(nếu nó tồn tại) hoặc .bash_profilelà. SHELLkhông được chỉ định bởi POSIX và thật không may, đây cũng không phải là cmdtùy chọn định dạng cho ps.
Anthony G - công lý cho Monica

1
Đây là những gì tôi sẽ sử dụng, nhưng nó cũng chịu sự thao túng độc hại.
dmckee

Lưu ý rằng trong khi ps -o cmd= $$nên hoạt động khá nhiều ở mọi nơi, $SHELLbiến là hoàn toàn không liên quan. Đó là shell mặc định của người dùng và không có liên quan đến shell nào hiện đang được chạy hoặc shell nào đang chạy script script.
terdon

2
Không thực sự, không. nhiều shell sẽ đọc ~/.profilekhi bắt đầu như shell đăng nhập. Vì vậy, ví dụ, bạn $SHELLcó thể cshvà bạn chạy bash -l. Điều đó sẽ bắt đầu bash như một vỏ đăng nhập, vì vậy nó sẽ đọc ~/.profile, nhưng bạn $SHELLvẫn sẽ trỏ đến csh. Thêm vào đó, .profilecó thể có nguồn gốc rõ ràng bởi một kịch bản khác. Vì OP sẽ cho sự mạnh mẽ tối đa, tất cả các loại trường hợp nên được xem xét. Trong mọi trường hợp, $SHELLkhông cung cấp bất kỳ thông tin hữu ích nào về trình bao hiện đang chạy.
terdon

2
FWIW, tôi cũng đã sửa lỗi - về việc SHELLkhông được chỉ định bởi POSIX: pubs.opengroup.org/onlinepub/9699919799/basingefs/ trộm
Anthony G - công lý cho Monica

4

Câu hỏi yêu cầu shell đăng nhập của người dùng cũng như shell hiện tại theo cách thức xuất hiện checkbashisms. Nếu đó có nghĩa là vỏ mà người dùng đăng nhập, tôi sẽ sử dụng lớp vỏ từ /etc/passwd, ví dụ:

MY_UID=$(id -u)
MYSHELL=$(awk -F: '$3 == '$MY_UID'{ print $7; }' </etc/passwd )

Người dùng tất nhiên có thể bắt đầu một shell mới sau khi đăng nhập. Tất nhiên, nếu một cái là bash và cái kia thì không, các thử nghiệm về các biến môi trường của bash có thể không giúp ích.

Một số có thể muốn sử dụng getentthay vì chỉ passwdtập tin (nhưng điều đó sẽ không nằm trong phạm vi của câu hỏi).

Dựa trên nhận xét về LDAP và đề xuất cho logname, hình thức thay thế này có thể được sử dụng:

MY_NAME=$(logname)
MYSHELL=$(getent passwd | awk -F: '$1 ~ /^'$MY_NAME'$/ {print $7;}' )

Trong khi thử nghiệm nó, tôi nhận thấy rằng lognamenó không thích chuyển hướng đầu vào của nó (vì vậy tôi đã tách biểu thức ra). Một chương trình kiểm tra nhanh getentsẽ hoạt động trên các nền tảng được đề cập (mặc dù điều đó nên được cung cấp trong câu hỏi ban đầu):


1
Giả sử cơ sở dữ liệu người dùng nằm trong / etc / passwd (không phải LDAP / NIS / mysql ...) và chỉ có một tên người dùng cho mỗi userid. (sẽ tốt hơn để kiểm tra cột đầu tiên chống lại $(logname)).
Stéphane Chazelas

iirc, POSIX không xác định tiện ích cần thiết (hoặc tôi sẽ sử dụng nó trong câu trả lời của mình). Cho dù sử dụng idhay một biến có thể sửa đổi người dùng là một lựa chọn khác nhau.
Thomas Dickey

1
Lưu ý rằng tôi đã nói $(logname), không phải $LOGNAME. Cả id người dùng và shell đăng nhập đều được lấy từ tên người dùng được sử dụng khi đăng nhập. Bạn không thể quay lại từ id người dùng về vỏ đăng nhập ngoại trừ trên các hệ thống chỉ có một tên người dùng cho mỗi id người dùng. Hoặc IOW, khóa chính trong cơ sở dữ liệu người dùng là tên người dùng, không phải uid.
Stéphane Chazelas

Cảm ơn @ThomasDickey cho một câu trả lời đến từ một góc nhìn khác. Tuy nhiên, nó phức tạp hơn một chút so với những gì tôi nghĩ (giống như câu trả lời của jimmijùi s và Stéphane ). Một trong những lý do khiến tôi đặt các tệp cấu hình shell của mình dưới sự kiểm soát phiên bản là để định cấu hình môi trường của tôi trên tất cả các hệ thống mà tôi hiện đang đăng nhập thường xuyên: Cygwin, Ubuntu và CentOS (cả 5 và 7, sử dụng Active Directory cho người dùng xác thực). Vì lý do đó, tôi thích giữ logic đơn giản nhất có thể.
Anthony G - công lý cho Monica
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.