#! / bin / sh vs #! / bin / bash cho khả năng di động tối đa

cherouvim 30/07/2017. 3 answers, 2.886 views
linux bash shell sh

Tôi thường làm việc với các máy chủ Ubuntu LTS mà từ những gì tôi hiểu symlink /bin/sh đến /bin/dash . Rất nhiều bản phân phối khác mặc dù symlink /bin/sh tới /bin/bash .

Từ đó tôi hiểu rằng nếu một tập lệnh sử dụng #!/bin/sh trên đầu nó có thể không chạy theo cùng một cách trên tất cả các máy chủ?

Có một thực hành được đề xuất mà vỏ để sử dụng cho các kịch bản khi một trong những muốn muốn tính di động tối đa của những kịch bản giữa các máy chủ?

1 Comments
Thorbjørn Ravn Andersen 01/08/2017
Có sự khác biệt nhỏ giữa các loại vỏ khác nhau. Nếu tính di động là quan trọng nhất đối với bạn thì hãy sử dụng #!/bin/sh và không sử dụng bất cứ thứ gì khác ngoài những gì mà shell gốc đã cung cấp.

3 Answers


Gordon Davisson 01/08/2017.

Có khoảng bốn mức độ di chuyển cho các kịch bản shell (theo như dòng shebang có liên quan):

  1. Di động nhất: sử dụng #!/bin/shonly sử dụng cú pháp shell cơ bản được chỉ định trong tiêu chuẩn POSIX . Điều này sẽ hoạt động trên khá nhiều hệ thống POSIX / unix / linux. (Vâng, ngoại trừ Solaris 10 và trước đó đã có vỏ Bourne di sản thực sự, trước POSIX nên không tuân thủ, như /bin/sh .)

  2. Thứ hai di động nhiều nhất: sử dụng một dòng #!/bin/bash (hoặc #!/usr/bin/env bash ) shebang và dính vào các tính năng bash v3. Điều này sẽ hoạt động trên bất kỳ hệ thống nào có bash (ở vị trí mong muốn).

  3. Phần ba di động nhiều nhất: sử dụng dòng #!/bin/bash (hoặc #!/usr/bin/env bash ) shebang và sử dụng các tính năng bash v4. Điều này sẽ thất bại trên bất kỳ hệ thống nào có bash v3 (ví dụ: macOS, phải sử dụng nó vì lý do cấp phép).

  4. Ít nhất di động: sử dụng #!/bin/sh shebang và sử dụng phần mở rộng bash cho cú pháp shell POSIX. Điều này sẽ thất bại trên bất kỳ hệ thống nào có thứ gì khác ngoài bash cho / bin / sh (chẳng hạn như các phiên bản Ubuntu gần đây). Đừng bao giờ làm điều này; nó không chỉ là một vấn đề tương thích, nó chỉ đơn giản là sai. Thật không may, đó là một lỗi rất nhiều người thực hiện.

Đề nghị của tôi: sử dụng bảo thủ nhất của ba đầu tiên cung cấp tất cả các tính năng vỏ mà bạn cần cho kịch bản. Đối với tính di động tối đa, hãy sử dụng tùy chọn # 1, nhưng trong kinh nghiệm của tôi một số tính năng bash (như mảng) đủ hữu ích để tôi đi với # 2.

Điều tồi tệ nhất bạn có thể làm là # 4, sử dụng sai shebang. Nếu bạn không chắc chắn những tính năng nào là POSIX cơ bản và là phần mở rộng bash, hoặc gắn bó với bash shebang (tức là lựa chọn # 2), hoặc kiểm tra kịch bản hoàn toàn bằng một trình bao rất cơ bản (như dấu gạch ngang trên máy chủ Ubuntu LTS). Ubuntu wiki có một danh sách tốt các bashisms để xem ra .

Có một số thông tin rất tốt về lịch sử và sự khác biệt giữa các shell trong câu hỏi Unix & Linux "Có nghĩa là gì sh tương thích?" và câu hỏi Stackoverflow "Sự khác biệt giữa sh và bash" .

Ngoài ra, hãy lưu ý rằng shell không phải là thứ duy nhất khác nhau giữa các hệ thống khác nhau; nếu bạn đã quen với Linux, bạn quen với các lệnh GNU, có nhiều phần mở rộng không chuẩn mà bạn không thể tìm thấy trên các hệ thống Unix khác (ví dụ: bsd, macOS). Thật không may, không có quy tắc đơn giản ở đây, bạn chỉ cần biết phạm vi biến thể cho các lệnh bạn đang sử dụng.

Một trong những lệnh nastiest về tính di động là một trong những điều cơ bản nhất: echo . Bất cứ khi nào bạn sử dụng nó với bất kỳ tùy chọn nào (ví dụ echo -n hoặc echo -e ), hoặc với bất kỳ dấu thoát (dấu gạch chéo ngược) nào trong chuỗi để in, các phiên bản khác nhau sẽ làm những việc khác nhau. Bất cứ lúc nào bạn muốn in một chuỗi mà không có một linefeed sau nó, hoặc với escape trong chuỗi, sử dụng printf thay vào đó (và tìm hiểu cách nó hoạt động - nó phức tạp hơn echo ). Lệnh ps cũng là một mớ hỗn độn .

Một điều chung khác để xem là các phần mở rộng gần đây / GNUish cho cú pháp tùy chọn lệnh: định dạng lệnh cũ (tiêu chuẩn) là lệnh được theo sau bởi các tùy chọn (với một dấu gạch ngang, và mỗi tùy chọn là một chữ cái duy nhất), tiếp theo là đối số lệnh. Các biến thể gần đây (và thường không di động) bao gồm các tùy chọn dài (thường được giới thiệu với -- ), cho phép các tùy chọn đến sau các đối số và sử dụng -- để tách các tùy chọn khỏi các đối số.

5 comments
12 pabouk 30/07/2017
Lựa chọn thứ tư chỉ đơn giản là một ý tưởng sai lầm. Xin vui lòng không sử dụng nó.
3 Gordon Davisson 30/07/2017
@pabouk Tôi đồng ý hoàn toàn, vì vậy tôi đã chỉnh sửa câu trả lời của mình để làm cho điều này rõ ràng hơn.
1 jlliagre 30/07/2017
Tuyên bố đầu tiên của bạn hơi gây hiểu lầm. Tiêu chuẩn POSIX không chỉ định bất cứ điều gì về việc shebang bên ngoài nói rằng việc sử dụng nó dẫn đến hành vi không xác định. Hơn nữa, POSIX không xác định vị trí vỏ posix phải được đặt, chỉ có tên của nó ( sh ), vì vậy /bin/sh không được bảo đảm là đường dẫn đúng. Di động nhất là sau đó không phải để chỉ định bất kỳ shebang ở tất cả, hoặc để thích ứng với shebang để hệ điều hành được sử dụng.
2 Michael Kjörling 30/07/2017
Tôi đã rơi vào # 4 với một kịch bản của tôi rất gần đây, và chỉ không thể hiểu tại sao nó không hoạt động; sau khi tất cả, các lệnh tương tự đã làm việc vững chắc, và đã làm chính xác những gì tôi muốn họ làm, khi tôi đã thử chúng trực tiếp trong trình bao. Ngay sau khi tôi thay đổi #!/bin/sh thành #!/bin/bash , kịch bản hoạt động hoàn hảo. (Để phòng thủ của tôi, kịch bản đó đã phát triển theo thời gian từ một trong số đó chỉ thực sự là sh-cõi, đối với một người dựa vào hành vi giống như bash.)
2 jlliagre 30/07/2017
@ MichaelKjörling (Bourne shell) thực sự là hầu như không bao giờ đi kèm với bản phân phối Linux và không tuân thủ POSIX. Vỏ tiêu chuẩn POSIX được tạo từ hành vi ksh , không phải Bourne. Những gì hầu hết các bản phân phối Linux theo FHS làm là có /bin/sh là một liên kết tượng trưng cho trình bao mà họ chọn để cung cấp khả năng tương thích POSIX, thường là dash hoặc dash .

Kaz 31/07/2017.

Trong tập lệnh ./configure chuẩn bị ngôn ngữ TXR để xây dựng, tôi đã viết phần mở đầu sau đây cho tính di động tốt hơn. Kịch bản lệnh sẽ tự khởi động ngay cả khi #!/bin/sh là một Bourne Shell không tuân theo POSIX. (Tôi xây dựng mọi bản phát hành trên một máy ảo Solaris 10).

#!/bin/sh

# use your own variable name instead of txr_shell;
# adjust to taste: search for your favorite shells

if test x$txr_shell = x ; then
  for shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $shell ; then
       txr_shell=$shell
       break
    fi
  done
  if test x$txr_shell = x ; then
    echo "No known POSIX shell found: falling back on /bin/sh, which may not work"
    txr_shell=/bin/sh
  fi
  export txr_shell
  exec $txr_shell $0 ${@+"$@"}
fi

# rest of the script here, executing in upgraded shell 

Ý tưởng ở đây là chúng ta tìm thấy một shell tốt hơn so với cái chúng ta đang chạy, và tái thực thi kịch bản bằng cách sử dụng shell đó. txr_shell môi trường txr_shell được thiết lập, do đó kịch bản lệnh được thực hiện lại biết nó là thể hiện đệ quy được thực hiện lại.

(Trong kịch bản của tôi, biến txr_shell cũng sau đó được sử dụng, cho chính xác hai mục đích: trước hết nó được in như một phần của thông báo thông tin trong đầu ra của tập lệnh. Thứ hai, nó được cài đặt như biến SHELL trong Makefile , để cũng sẽ sử dụng shell này để thực hiện các công thức.)

Trên một hệ thống mà /bin/sh là dấu gạch ngang, bạn có thể thấy rằng logic trên sẽ tìm thấy /bin/bash và thực thi lại tập lệnh đó.

Trên hộp Solaris 10, /usr/xpg4/bin/sh sẽ khởi động nếu không tìm thấy Bash.

Phần mở đầu được viết bằng phương ngữ vỏ bảo thủ, sử dụng test sự tồn tại của tệp và mẹo ${@+"$@"} để mở rộng các đối số phục vụ cho một số shell cũ bị hỏng (đơn giản là "$@" nếu chúng ta trong một vỏ phù hợp POSIX).

2 comments
Charles Duffy 31/07/2017
Người ta sẽ không cần x hackery nếu trích dẫn thích hợp được sử dụng, vì các tình huống bắt buộc thành ngữ đó bao quanh các lời gọi thử nghiệm không được chấp nhận với -a hoặc -o kết hợp nhiều thử nghiệm.
Kaz 31/07/2017
@CharlesDuffy Thật vậy; các test x$whatever mà tôi đang perpetrating có vẻ giống như một hành tây trong véc ni. Nếu chúng ta không thể tin tưởng vào vỏ cũ bị hỏng để trích dẫn thì nỗ lực cuối cùng ${@+"$@"} là vô nghĩa.

zwol 31/07/2017.

Tất cả các biến thể của ngôn ngữ shell Bourne là khách quan khủng khiếp so với các ngôn ngữ kịch bản hiện đại như Perl, Python, Ruby, node.js, và thậm chí (có thể cho là) ​​Tcl. Nếu bạn phải làm bất cứ điều gì thậm chí một chút phức tạp, bạn sẽ được hạnh phúc hơn trong thời gian dài nếu bạn sử dụng một trong những điều trên thay vì một kịch bản shell.

Một và lợi thế duy nhất ngôn ngữ shell vẫn có trên những ngôn ngữ mới hơn là something gọi chính nó /bin/sh được đảm bảo tồn tại trên bất cứ điều gì mà purports là Unix. Tuy nhiên, điều gì đó thậm chí có thể không tuân thủ POSIX; nhiều Unixes độc quyền cũ đã đóng băng ngôn ngữ được thực hiện bởi /bin/sh và các tiện ích trong PATH mặc định prior các thay đổi được yêu cầu bởi Unix95 (có, Unix95, hai mươi năm trước và đếm). Có thể có một tập hợp các Unix95, hoặc thậm chí POSIX.1-2001 nếu bạn may mắn, các công cụ trong một thư mục not trên PATH mặc định (ví dụ /usr/xpg4/bin ) nhưng chúng không được bảo đảm tồn tại.

Tuy nhiên, những điều cơ bản của Perl có more likely xuất hiện trên một cài đặt Unix được chọn tùy ý hơn là Bash. (Theo "những điều cơ bản của Perl" tôi có nghĩa là /usr/bin/perl tồn tại và là some , có thể khá cũ, phiên bản của Perl 5, và nếu bạn may mắn, tập hợp các mô-đun đi kèm với phiên bản phiên dịch đó cũng có sẵn.)

Vì thế:

Nếu bạn đang viết một cái gì đó mà phải làm việc everywhere mà purports là Unix (chẳng hạn như một "cấu hình" kịch bản), bạn cần phải sử dụng #! /bin/sh #! /bin/sh , và bạn không cần sử dụng bất kỳ phần mở rộng nào. Ngày nay tôi sẽ viết vỏ tuân thủ POSIX.1-2001 trong trường hợp này, nhưng tôi sẽ chuẩn bị vá lỗi POSIX nếu ai đó yêu cầu hỗ trợ cho sắt gỉ.

Nhưng nếu bạn not viết một cái gì đó mà phải làm việc ở khắp mọi nơi, thì thời điểm bạn bị cám dỗ để sử dụng bất kỳ Bashism ở tất cả, bạn nên dừng lại và viết lại toàn bộ điều trong một ngôn ngữ kịch bản tốt hơn để thay thế. Tương lai của bạn sẽ cảm ơn bạn.

(Vì vậy, khi nào nó thích hợp để sử dụng các phần mở rộng Bash? Để thứ tự đầu tiên: không bao giờ. Để thứ hai: chỉ để mở rộng môi trường tương tác Bash - ví dụ để cung cấp thông báo hoàn thành tab và các lời nhắc ưa thích.)

Related questions

Hot questions

Language

Popular Tags