Tại sao các tiện ích bắt buộc POSIX không được tích hợp vào vỏ?


45

Mục đích của câu hỏi này là để trả lời một sự tò mò, không phải để giải quyết một vấn đề điện toán cụ thể. Câu hỏi là: Tại sao các tiện ích bắt buộc POSIX không được tích hợp phổ biến trong triển khai shell?

Ví dụ: tôi có một đoạn script về cơ bản đọc một vài tệp văn bản nhỏ và kiểm tra xem chúng có được định dạng chính xác không, nhưng phải mất 27 giây để chạy, trên máy của tôi, do một số lượng thao tác chuỗi đáng kể. Thao tác chuỗi này tạo ra hàng ngàn quy trình mới bằng cách gọi các tiện ích khác nhau, do đó chậm. Tôi khá tự tin rằng nếu một số các tiện ích được xây dựng trong, cụ thể là grep, sed, cut, tr, và expr, sau đó kịch bản sẽ chạy trong một giây hoặc ít hơn (dựa trên kinh nghiệm của tôi trong C).

Dường như sẽ có rất nhiều tình huống trong đó việc xây dựng các tiện ích này sẽ tạo ra sự khác biệt giữa việc một giải pháp trong shell script có hiệu suất chấp nhận được hay không.

Rõ ràng, có một lý do khiến nó được chọn không tích hợp các tiện ích này. Có thể có một phiên bản tiện ích ở cấp hệ thống để tránh việc có nhiều phiên bản không đồng đều của tiện ích đó được sử dụng bởi các trình bao khác nhau. Tôi thực sự không thể nghĩ ra nhiều lý do khác để duy trì quá trình tạo ra nhiều quy trình mới và POSIX định nghĩa đủ về các tiện ích mà dường như không có vấn đề gì khi có các triển khai khác nhau, miễn là chúng là mỗi POSIX tuân thủ. Ít nhất không phải là một vấn đề lớn như sự không hiệu quả của việc có quá nhiều quá trình.


15
Nếu 27 giây quá chậm, bạn có thể sử dụng Python, Perl hoặc một số ngôn ngữ được biên dịch bán khác. Hoặc đăng các phần chậm của tập lệnh của bạn và yêu cầu cải tiến. Có thể là bạn đang sử dụng ba hoặc bốn lệnh trong đó một (một nhanh hơn) có thể làm.
roaima

8
Shell không thực sự được thực hiện cho các nhiệm vụ nặng nề, thật không may và thế giới đã thay đổi rất nhiều kể từ thời điểm bạn có thể thoát khỏi chỉ với một kịch bản shell. Tôi đồng ý với roaima - mọi sysadmin hợp lý nên dùng Python hoặc Perl và không mong đợi cái vỏ sẽ xử lý mọi thứ
Sergiy Kolodyazhnyy

16
Mục đích chính của shell là chạy các chương trình khác, không thao tác trực tiếp với dữ liệu. Trong những năm qua, một số chương trình hoặc tính năng bên ngoài do chúng cung cấp (toàn cầu, số học printf, v.v.) đã được tích hợp vào hệ vỏ khi chúng được coi là đủ hữu ích.
chepner

8
Nếu bạn đăng tập lệnh của mình lên codereview.stackexchange.com, tôi chắc chắn những người đánh giá có thể đưa ra một số đề xuất để tăng tốc độ kịch bản của bạn một cách quyết liệt (hoặc ít nhất là chỉ ra lý do tại sao nên viết bằng Python / etc thay vì shell).
chepner

5
@ Kyle: awklà một tiện ích bắt buộc trong POSIX, và đặc biệt là rất thích hợp (có nghĩa là, rất nhanh) để thực hiện kịch bản mà bạn nếu không thể thực hiện sử dụng sed, cut, tr, grep, và exprtrong một kịch bản shell.
Động vật danh nghĩa

Câu trả lời:


11

Các kịch bản Shell dự kiến ​​sẽ không chạy với loại tốc độ đó. Nếu bạn muốn cải thiện tốc độ của tập lệnh của mình, hãy thử nó trong perl. Nếu vẫn còn quá chậm, thì bạn sẽ phải chuyển sang ngôn ngữ được nhập tĩnh như java hoặc c hoặc viết mô-đun C cho perl chạy các phần quá chậm.

Shell là cấp độ tạo mẫu đầu tiên, nếu bạn có thể chứng minh khái niệm bằng shell, sau đó chuyển sang một ngôn ngữ kịch bản tốt hơn để có thể kiểm tra nhiều giới hạn hơn sẽ mất nhiều mẫu vỏ.

Một hệ điều hành Unix dự kiến ​​sẽ bao gồm nhiều chương trình nhỏ thực hiện tốt các nhiệm vụ tạo nên một bức tranh lớn hơn. Đây là một điều tốt vì nó ngăn chặn các chương trình lớn hơn. Hãy xem qmail, ví dụ và so sánh với sendmail. qmail được tạo thành từ nhiều chương trình:

http://www.nrg4u.com/qmail/the-big-qmail-picture-103-p1.gif

Khai thác daemon mạng sẽ không giúp bạn khai thác trình quản lý hàng đợi.


OP đặc biệt KHÔNG yêu cầu đề xuất cải thiện tốc độ của mã. Câu hỏi là tại sao một số tiện ích nhất định không được tích hợp sẵn như cdhoặc pwd.
Stephen C

4
Thật. Câu trả lời là thể hiện sự khác biệt giữa nguyên khối và ngăn cách và cho thấy một lý do có lợi cho việc này.
Ed Neville


1
@StephenC cdlà một nội trang - và thực tế nó phải như vậy, bởi vì việc thay đổi thư mục làm việc trong một quy trình con không ảnh hưởng đến các quy trình cha.
Jonas

67

Tại sao các tiện ích bắt buộc POSIX không được tích hợp vào vỏ?

Bởi vì để tuân thủ POSIX, một hệ thống được yêu cầu 1 để cung cấp hầu hết các tiện ích dưới dạng các lệnh độc lập.

Có chúng dựng sẵn sẽ ngụ ý rằng chúng phải tồn tại ở hai địa điểm khác nhau, bên trong vỏ và bên ngoài nó. Tất nhiên, có thể triển khai phiên bản bên ngoài bằng cách sử dụng trình bao bọc kịch bản lệnh shell cho phần dựng sẵn, nhưng điều đó sẽ gây bất lợi cho các ứng dụng không phải trình bao gọi các tiện ích.

Lưu ý rằng BusyBox đã chọn đường dẫn mà bạn đề xuất bằng cách triển khai nhiều lệnh bên trong và cung cấp biến thể độc lập bằng cách sử dụng các liên kết đến chính nó. Một vấn đề là trong khi bộ lệnh có thể khá lớn, việc triển khai thường là một tập hợp con của tiêu chuẩn nên không tuân thủ.

Cũng lưu ý rằng ít nhất ksh93, bashzshđi xa hơn bằng cách cung cấp các phương thức tùy chỉnh cho shell đang chạy để tải động các nội dung từ các thư viện dùng chung. Về mặt kỹ thuật, sau đó không có gì ngăn cản tất cả các tiện ích POSIX được triển khai và cung cấp dưới dạng nội dung.

Cuối cùng, sinh ra các quy trình mới đã trở thành một hoạt động khá nhanh với các hệ điều hành hiện đại. Nếu bạn thực sự gặp phải vấn đề về hiệu năng, có thể có một số cải tiến để làm cho tập lệnh của bạn chạy nhanh hơn.

1 POSIX.1-2008

Tuy nhiên, tất cả các tiện ích tiêu chuẩn , bao gồm các tiện ích tích hợp thông thường trong bảng, nhưng không phải là các tiện ích tích hợp đặc biệt được mô tả trong Tiện ích tích hợp đặc biệt, sẽ được triển khai theo cách để chúng có thể được truy cập thông qua họ exec các chức năng như được xác định trong khối lượng Giao diện hệ thống của POSIX.1-2008 và có thể được gọi trực tiếp bởi các tiện ích tiêu chuẩn yêu cầu nó (env, find, beautiful, nohup, time, xargs).


4
Đây là câu trả lời đúng, nhưng tôi chỉ nói thêm rằng giao diện của các tiện ích này thường thông qua stdin / stdout, rằng ngay cả khi mỗi một trong số chúng cũng được thực hiện như một thói quen tích hợp trong bash, nó vẫn cần một cách hiệu quả để tự rẽ nhánh và tạo đường ống cho mỗi lệnh trong một đường ống, vì vậy sẽ chỉ có lợi nhuận cận biên
Chunko

2
@Chunko Vâng. subshells nhẹ hơn các quá trình fork / exec'ed.
jlliagre

3
@slebetman Bạn đang thiếu quan điểm của tôi. Subshells không phải là các luồng cũng như các tiến trình được thực thi, bất kể chúng có chạy trên Linux hay không. Subshells chỉ là bản sao của cha mẹ chúng, được tạo bởi fork không theo sau exec; forkngày nay là một hoạt động rất nhẹ so với exec.
jlliagre

3
Tôi đã đo các noforknội dung của busybox là có thứ tự ít hơn 10 lần so với các noexecnội trang, do đó có tổng chi phí ít hơn ~ 5x so với fork + exec của một nhị phân riêng biệt. Các định nghĩa theo unix.stackexchange.com/a/274322/29483 Thật thú vị khi busybox không có noforkgì cả, mặc dù tôi biết một số mã busybox được rút ngắn bằng cách không dọn dẹp bộ nhớ và chỉ dựa vào quá trình tồn tại ngắn.
sourcejedi

1
@jlliagre: Trên linux một ngã ba tạo ra một quy trình. Điểm bạn có lẽ còn thiếu là trên Linux, họ đã tối ưu hóa các quy trình đến mức các nhà phát triển đã xác định rằng không có lợi thế hơn nữa khi tạo ra bất cứ thứ gì nhẹ hơn. Về cơ bản trong linux một quá trình nhẹ như một luồng.
slebetman

9

Từ hướng dẫn tham khảo BASH ,

Các lệnh dựng sẵn là cần thiết để thực hiện chức năng không thể hoặc bất tiện để có được với các tiện ích riêng biệt.

Như tôi chắc chắn bạn đã nghe nói, triết lý UNIX phụ thuộc rất nhiều vào nhiều ứng dụng mà tất cả đều có chức năng hạn chế. Mỗi tích hợp có một lý do rất chính đáng tại sao nó được tích hợp. Mọi thứ khác thì không. Tôi nghĩ rằng một lớp câu hỏi thú vị hơn nằm dọc theo dòng chữ, "tại sao chính xác lại được tích pwd hợp?"


2
Trong một từ: Modularity
Peschke

2
/ bin / pwd tồn tại. Tôi nghĩ rằng đây cdsẽ là một ví dụ tốt hơn ở đây về một thứ không thể thực hiện như một công cụ riêng biệt.
Oskar Skog

1
@OskarSkog Đó là điểm chính. cdphải được xây dựng trong, pwdkhông. Vậy tại sao những người bashthực hiện lại chọn đưa nó vào?
Stig Hemmer

1
... được bao phủ bởi unix.stackexchange.com/questions/145479 .
JdeBP

@StigHemmer /bin/bashkhông tồn tại, nhưng nó vẫn là một nội dung. Xem danh sách các nội dung tại gnu.org/software/bash/manual/html_node/ Kẻ
Stephen C

8

Những người ở AT & T đã tự hỏi mình điều tương tự

Nếu bạn nhìn vào lịch sử của Bộ công cụ phần mềm AT & T (hiện đang nằm im trên github kể từ khi nhóm nòng cốt rời đi), đây chính xác là những gì họ đã làm với vỏ AT & T Korn, còn gọi là ksh93.

Hiệu suất luôn là một phần động lực cho các nhà bảo trì ksh93 và khi xây dựng ksh, bạn có thể chọn xây dựng nhiều tiện ích POSIX phổ biến như các thư viện được tải động. Bằng cách ràng buộc các lệnh này với một tên thư mục như thế nào /opt/ast/bin, bạn có thể kiểm soát phiên bản lệnh nào sẽ được sử dụng, dựa trên vị trí của tên thư mục đó $PATH.

Ví dụ:

cat chmod chown cksum cmp cp cut date expr fmt head join ln
mkdir mkfifo mktemp mv nl od paste rm tail tr uniq uuencode wc

Danh sách đầy đủ có thể được tìm thấy trong kho github ast .

Lưu ý rằng hầu hết các công cụ ast đều có nguồn gốc riêng và sẽ khác biệt mạnh mẽ với các triển khai gnu phổ biến hơn. Nhóm nghiên cứu AT & T tuân thủ các tiêu chuẩn chính thức, đó là cách để đạt được khả năng tương tác khi bạn không thể chia sẻ mã.


6

Vì vậy, chúng tôi đã không sắp xếp các nguồn lực để tối ưu hóa công cụ ban đầu, để đáp ứng mọi mong muốn cụ thể. Tôi đoán những gì chúng ta cần giải thích là mong muốn cụ thể này sẽ có chi phí bao nhiêu để thực hiện.

POSIX định nghĩa đủ về các tiện ích mà dường như không có vấn đề gì khi có các triển khai khác nhau.

đây là một giả định tồi :-P.

Các hệ thống sau POSIX tiếp tục trở nên mạnh mẽ và thuận tiện hơn vì những lý do chính đáng; như một tiêu chuẩn sau thực tế, nó không bao giờ thực sự bắt kịp.

Ubuntu đã bắt đầu một nỗ lực để chuyển sang trình bao POSIX rút gọn cho các tập lệnh, để tối ưu hóa quy trình khởi động System V init cũ. Tôi không nói rằng nó thất bại, nhưng nó đã kích hoạt nhiều lỗi phải được dọn sạch: "bashism", các tập lệnh chạy /bin/shtrong khi cho rằng bashcác tính năng đã có sẵn.

POSIX sh không phải là một ngôn ngữ lập trình đa năng tốt. Mục đích chính của nó là để làm việc tốt như một vỏ tương tác. Ngay khi bạn bắt đầu lưu các lệnh của mình vào một tập lệnh, hãy lưu ý rằng bạn tiếp cận một tarpit Turing . Ví dụ, không thể phát hiện ra các lỗi ở giữa một đường ống thông thường . bashđược thêm vào set -o pipefailcho điều này, nhưng điều này không có trong POSIX.

Các tính năng hữu ích tương tự nhưng không đạt tiêu chuẩn được cung cấp bởi hầu hết mọi tiện ích phức tạp hơn true.

Đối với lớp nhiệm vụ bạn phác thảo, bạn có thể vẽ một đường thô cho Awk, Perl và ngày nay là Python. Các công cụ khác nhau đã được tạo ra, và phát triển độc lập. Bạn có mong muốn, ví dụ GNU Awk sẽ được đưa vào một libutilposeixextends không?

Tôi không nói rằng bây giờ chúng ta có một cách tiếp cận tốt hơn mà tôi có thể chỉ cho bạn. Tôi có một điểm mềm cho Python. Awk mạnh đến mức đáng ngạc nhiên, mặc dù tôi đã thất vọng vì một số tính năng dành riêng cho GNU Awk. Nhưng vấn đề là việc xử lý số lượng lớn các chuỗi riêng lẻ (có lẽ là từ các dòng của tệp) không phải là mục tiêu thiết kế của vỏ POSIX.


Tôi tự hỏi liệu có bất kỳ khó khăn nào với shell mà sẽ cho rằng bất kỳ lệnh nào được thực thi từ danh sách các vị trí có thể định cấu hình sẽ được coi là tích hợp trong trường hợp shell hiểu mọi thứ về lệnh không? Nếu một tập lệnh thực hiện cat -@fnord fooshell sẽ quyết định rằng vì nó không biết điều gì -@có nghĩa là nó sẽ cần phải gọi lệnh thực tế, nhưng chỉ cần cat <foo >barshell nên không cần sinh ra một quy trình khác.
supercat

1
@supercat phức tạp.
nguồn

2

Ngoài ra còn có câu hỏi: bạn sẽ xây dựng nó vào cái vỏ nào?

Hầu hết các hệ thống Unix / Linux có nhiều hệ vỏ khác nhau được phát triển độc lập (sh / bash / korn / ???). Nếu bạn xây dựng các công cụ vào trình bao, bạn sẽ kết thúc với việc triển khai các công cụ này cho mỗi trình bao khác nhau. Điều này sẽ gây ra chi phí và bạn có thể kết thúc với các tính năng / lỗi khác nhau, ví dụ như grep, tùy thuộc vào loại vỏ bạn đã sử dụng để gọi nó.


zsh là khá phổ biến trong một số vòng tròn những ngày này. csh / tcsh trong lịch sử đã có một lượng lớn người theo dõi, nhưng tôi không nghĩ rằng bạn thấy nhiều về nó ngày hôm nay. Và có cả một nhóm đạn pháo ít được biết đến ...
một CVn

Tính mô đun. Với các nội trang, bạn cần biên dịch lại hoặc cài đặt lại vỏ mỗi khi có thay đổi đối với một trong các nội dung đó.
can-ned_food

1

Nhiều người đã trả lời tốt. Tôi dự định chỉ khen những câu trả lời. Tôi nghĩ triết lý UNIX là một công cụ nên làm một việc và làm tốt. Nếu một người cố gắng tạo ra một công cụ bao gồm tất cả, đó sẽ là nơi thất bại nhiều hơn. Giới hạn chức năng theo cách này làm cho một bộ công cụ đáng tin cậy.

Ngoài ra, hãy xem xét, nếu chức năng như sed hoặc grep được tích hợp vào shell, nó có dễ dàng được gọi từ dòng lệnh khi bạn muốn không?

Cuối cùng, hãy xem xét, một số chức năng bạn mong muốn có trong BASH, là trong BASH . Ví dụ: khả năng khớp RE trong BASH được triển khai bằng toán tử nhị phân = ~ (xem Shell Grammar trong Trang thủ công để biết thêm, cụ thể, tham khảo thảo luận về cấu trúc [[]] cho if ). Ví dụ rất nhanh, giả sử tôi đang tìm kiếm một tệp có 2 chữ số hex:

while read line; do
    if [[ $line =~ 0x[[:xdigit:]]{2} ]]; then
        # do something important with it
    fi
done < input_file.txt

Đối với chức năng giống như sed , hãy xem phần Mở rộng tham số trong tiêu đề Mở rộng của cùng một trang người đàn ông. Bạn sẽ thấy vô số điều bạn có thể làm mà gợi nhớ đến sed. Tôi thường sử dụng sed để thực hiện một số thay thế loại văn bản. Xây dựng những điều trên:

# this does not take into account the saving of the substituted text
# it shows only how to do it
while read line; do
    ${line/pattern/substitution}
done < input_file.txt

Cuối cùng, mặc dù "tốt hơn" ở trên?

grep -E "[[:xdigit:]]{3}" input_file.txt
sed -e 's/pattern/substitution/' input_file.txt

Có thể tìm thấy một đối số chống lại câu hỏi cuối cùng trong unix.stackexchange.com/questions/169716/ mẹo
phk 18/03/17

1

Đây là, tôi đoán, một tai nạn lịch sử.

Khi UNIX được tạo ra vào cuối những năm 1960 và đầu những năm 1970, máy tính không có nhiều bộ nhớ như ngày nay. Vào thời điểm đó, có thể đã thực hiện tất cả chức năng này dưới dạng các phần tử shell, nhưng do hạn chế về bộ nhớ, họ sẽ phải giới hạn số lượng chức năng mà họ có thể thực hiện hoặc thoát khỏi bộ nhớ và / hoặc trao đổi rác các vấn đề.

Mặt khác, bằng cách triển khai chức năng đã cho dưới dạng các chương trình riêng biệt và bằng cách thực hiện hai lệnh gọi hệ thống cần thiết để bắt đầu một quy trình mới càng nhẹ càng tốt, chúng có thể tạo ra một môi trường kịch bản không có những vấn đề đó và vẫn chạy hợp lý tốc độ.

Tất nhiên, một khi những điều đó được thực hiện dưới dạng các quy trình riêng biệt, mọi người sẽ khởi động chúng từ các chương trình không phải là trình bao , và sau đó chúng phải duy trì như vậy hoặc đột nhiên tất cả phần mềm này bắt đầu bị hỏng.

Tuy nhiên, điều đó không có nghĩa là bạn không thể thực hiện một số chức năng hai lần, và thực tế, một số trình bao thực hiện một số chức năng được coi là một chương trình bên ngoài dưới dạng trình bao; ví dụ: bash thực hiện echolệnh dưới dạng dựng sẵn, nhưng cũng có một/usr/bin/echo

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.