Có phải là phổ biến để phân chia tập lệnh lớn hơn thành nhiều tập lệnh và nguồn chúng trong tập lệnh chính?


23

Hiện tại tôi đang phát triển một tập lệnh Bash lớn hơn (đó là một dự án Nguồn mở của tôi) và nó bắt đầu trở thành một mớ hỗn độn. Tôi đã phân tách logic thành các hàm, sử dụng các biến cục bộ nơi tôi có thể và chỉ khai báo một số biến toàn cục. Tuy nhiên, nó đang trở nên khá khó để duy trì.

Tôi đã nghĩ về việc chia tập lệnh thành nhiều tập lệnh và lấy chúng trong tập lệnh chính của tôi (tương tự như nhập bằng các ngôn ngữ khác).

Nhưng tôi tự hỏi nếu đây là một phương pháp khả thi. Thứ nhất, tìm nguồn cung ứng nhiều tập lệnh có thể làm chậm đáng kể thời gian thực hiện tập lệnh và thứ hai, nó làm cho việc phân phối trở nên khó khăn hơn.

Vì vậy, đây có phải là một cách tiếp cận tốt và các dự án (Nguồn mở) khác có làm theo cách tương tự không?


4
Tôi đã tìm kiếm một kịch bản shell thực sự dài và ở 3500 dòng và 125KB, tôi không muốn cố gắng duy trì nó. Shell thực sự thân thiện khi kết nối các chương trình, nhưng khi nó cố gắng tính toán, nó trở nên khá xấu xí. Tôi biết mã bạn có hầu hết hoạt động và chi phí chuyển nó cao, nhưng bạn có thể muốn xem xét một cái gì đó khác trong tương lai.
msw

1
Tôi đã gặp vấn đề tương tự. Tôi có một dự án bash lớn vừa phải sourceforge.net/projects/duplexpr . Hiện tại, mỗi tập lệnh đều được chứa, nhưng tôi đang nghĩ đến việc chuyển tất cả các chức năng phổ biến sang một tệp riêng biệt và bao gồm chúng khi cần loại bỏ việc phải cập nhật chúng ở nhiều nơi. Nói chung, tôi nghĩ sẽ tốt hơn nếu chỉ gọi mỗi kịch bản kế tiếp chứ không phải là nguồn. Nó làm cho việc chạy các bộ phận độc lập có thể. Sau đó, bạn phải chuyển các biến dưới dạng đối số hoặc trong tệp tham số (hoặc bạn chỉ có thể xuất chúng nếu bạn không muốn độc lập.)
Joe

Câu trả lời:


13

Vâng, đó là một thực tế phổ biến. Ví dụ, trong những ngày đầu của Unix, mã shell chịu trách nhiệm hướng dẫn hệ thống thông qua các giai đoạn khởi động của nó cho hoạt động của nhiều người dùng là một tệp duy nhất , /etc/rc. Ngày nay, quá trình khởi động được kiểm soát bởi nhiều tập lệnh shell, được chia theo chức năng, với các hàm và biến phổ biến có nguồn gốc khi cần từ các vị trí trung tâm. Các bản phân phối Linux, Mac, BSD, tất cả đã áp dụng phương pháp này ở mức độ khác nhau.


2
mặc dù trong trường hợp đó, nó có thể tái sử dụng nhiều hơn. Các tập lệnh khác nhau sử dụng một thư viện chung của các hàm shell.
Stéphane Chazelas

16

Shell có phải là công cụ phù hợp cho công việc tại thời điểm đó không? Là một nhà phát triển đã gặp phải các vấn đề về mã phát triển, tôi có thể nói với bạn rằng không nên xem xét viết lại mà thay vào đó hãy xem xét tách các phần thành thứ gì đó phù hợp hơn với quy mô mà bạn đang tìm kiếm để phát triển ứng dụng của mình - có thể là python hoặc ruby ​​hoặc thậm chí là perl ?

Shell là một ngôn ngữ tiện ích - đó là ngôn ngữ kịch bản - và do đó, sẽ khó phát triển nó đến các kích thước đó.


Đây chính xác là những gì tôi sắp đăng.
zwol

đặc biệt là ngay cả các hệ điều hành cũ hơn như Solaris đang vận chuyển với perl và python, v.v., bây giờ. Một lý do khiến các hệ thống cũ sử dụng tập lệnh shell là shell được đảm bảo luôn có sẵn, nhưng các Unice lớn hơn, như HP-UX và Solaris, và AIX, không thể được đảm bảo bao gồm các công cụ khác.
Tim Kennedy

7

Nếu nó làm cho bảo trì của bạn dễ dàng hơn, bạn có thể có cả hai. Chia nó thành các phần logic để bạn có thể duy trì dễ dàng, sau đó viết (ví dụ) Makefile để đặt lại tất cả lại với nhau để phân phối Bạn có thể viết một số tập lệnh nhanh để sao chép các hàm từ tệp đính kèm sang tệp đầu ra thay cho các sourcedòng hoặc chỉ làm điều gì đó tầm thường như thế này (bạn sẽ phải làm lại tabify này, vì makeđòi hỏi các tab):

all: myscript

myscript: includes/* body/*
    cat $^ > "$@" || (rm -f "$@"; exit 1)

Sau đó, bạn có phiên bản "nguồn" (được sử dụng để chỉnh sửa) và phiên bản "nhị phân" (được sử dụng để cài đặt tầm thường).


1
Aha! Một người khác sử dụng phương pháp mèo đơn giản :)
Clayton Stanley

4

Một kịch bản có thể bị phá vỡ như bạn mô tả - bất cứ điều gì có thể được thực hiện. Tôi sẽ nói rằng 'cách tiếp cận tốt' sẽ là ngăn chặn tập lệnh lớn của bạn, tìm ra nơi các phần của nó có thể được chạy như một quy trình riêng biệt, giao tiếp thông qua các cơ chế IPC.

Ngoài ra, đối với một tập lệnh shell, tôi sẽ gói nó thành một tập tin duy nhất. Như bạn nói, nó làm cho việc phân phối trở nên khó khăn hơn: bạn phải biết các tập lệnh của 'thư viện' nằm ở đâu - không có tiêu chuẩn đẹp nào cho các tập lệnh shell - hoặc dựa vào người dùng để đặt đường dẫn chính xác.

Bạn có thể phân phối một chương trình cài đặt xử lý tất cả điều này cho bạn, giải nén các tệp, đặt chúng vào đúng vị trí, yêu cầu người dùng thêm một cái gì đó giống như export PROGRAMDIR=$HOME/lib/PROGRAMvào tệp ~ / .bashrc. Sau đó, chương trình chính có thể thất bại nếu $PROGRAMDIRkhông được đặt hoặc không chứa các tệp bạn mong đợi.

Tôi sẽ không lo lắng nhiều về chi phí tải các tập lệnh khác. Chi phí thực sự chỉ là mở một tập tin; việc xử lý văn bản là như nhau, đặc biệt nếu chúng là các định nghĩa hàm.


1

Thực tế thông thường hay không, tôi không nghĩ tìm nguồn cung ứng bất cứ thứ gì ngoài một bộ hàng xuất khẩu là một ý tưởng hay. Tôi thấy rằng việc thực thi mã bằng cách tìm nguồn cung cấp chỉ là khó hiểu và nó hạn chế sử dụng lại, bởi vì các cài đặt biến môi trường và biến khác làm cho mã nguồn phụ thuộc rất nhiều vào mã nguồn.

Bạn nên chia nhỏ ứng dụng của mình thành các tập lệnh nhỏ hơn, khép kín, sau đó thực thi chúng dưới dạng một loạt các lệnh. Điều này sẽ dễ dàng gỡ lỗi, vì bạn có thể thực thi từng tập lệnh độc lập trong một vỏ tương tác, kiểm tra các tệp, nhật ký, vv giữa mỗi lần gọi lệnh. Tập lệnh ứng dụng lớn, đơn của bạn biến thành tập lệnh kiểm soát đơn giản hơn, chỉ chạy một loạt lệnh sau khi bạn đã gỡ lỗi xong.

Một nhóm các tập lệnh nhỏ hơn, độc lập chạy vào vấn đề khó cài đặt hơn.


1

Một cách khác để tìm nguồn cung ứng các tập lệnh chỉ đơn giản là gọi chúng bằng các đối số. Nếu bạn đã chia nhỏ hầu hết các chức năng của mình thành các hàm shell, có lẽ bạn đã khá gần với khả năng thực hiện điều này rồi. Đoạn mã sau đây của Bash cho phép mọi hàm được khai báo trong tập lệnh được sử dụng như một tiểu ban:

if [[ ${1:-} ]] && declare -F | cut -d' ' -f3 | fgrep -qx -- "${1:-}"
then "$@"
else main "$@" # Try the main function if args don't match a declaration.
fi

Lý do không phải sourcelà để tránh ô nhiễm môi trường và tùy chọn.


0

Dưới đây là ví dụ của tôi về cách một tập lệnh bash lớn có thể được chia thành nhiều tệp và sau đó được tích hợp vào một tập lệnh kết quả: https://github.com/zinovyev/bash-project

Tôi sử dụng một Makefilecho mục đích này:

TARGET_FILE = "target.sh"
PRJ_SRC = "${PWD}/src/main.sh"
PRJ_LIB = $(shell ls -d ${PWD}/lib/*) # All files from ./lib

export PRJ_LIB

SHELL := /bin/env bash
all: define_main add_dependencies invoke_main

define_main:
    echo -e "#!/usr/bin/env bash\n" > ${TARGET_FILE}
    echo -e "function main() {\n" >> ${TARGET_FILE}
    cat "${PRJ_SRC}" | sed -e 's/^/  /g' >> ${TARGET_FILE}
    echo -e "\n}\n" >> ${TARGET_FILE}

invoke_main:
    echo "main \$$@" >> ${TARGET_FILE}

add_dependencies:
    for filename in $${PRJ_LIB[*]}; do cat $${filename} >> ${TARGET_FILE}; echo >> ${TARGET_FILE}; done
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.