Trả lại danh sách từ python sang shell script

Chúng ta có thể nói về chiếc áo hoodie cũ mà bạn đang mặc không? . Vâng, nó giữ cho mặt trời khỏi đầu bạn. Mặc dù vậy, việc giữ các túi lại với nhau bằng băng keo dường như không thực sự tối ưu. Và có cái lỗ ở khuỷu tay. Và vết bẩn đó là gì. Nếu bạn giặt nó, liệu có còn sót lại gì không…

Hãy thừa nhận điều đó, một ứng dụng trông giống như một chiếc áo hoodie miễn phí từ một trang web không còn tồn tại không tạo ra nhiều sự tự tin. Ứng dụng trông như thế nào là quan trọng, giống như với quần áo. Vì vậy, rất nhiều phần mềm mang lại trải nghiệm hào nhoáng, lạ mắt nhưng tất cả chúng ta đều biết phần mềm có nhiều thứ hơn là những gì bắt mắt. Dưới bề ngoài đó có các lớp xử lý mà người dùng không nhìn thấy. Có tất cả các loại nút, khóa kéo, thắt lưng và dây treo để đảm bảo mọi thứ được giữ chặt với nhau

Chẳng hạn, người vận hành kỹ thuật [TechOps] phải khởi động máy chủ, xác nhận rằng nó đang chạy, dừng máy chủ, có thể sao lưu dữ liệu, có thể xuất hoặc nhập dữ liệu. Ngoài các hoạt động, những người phát triển [DevOps] có một bộ công cụ để tích hợp và triển khai. Công cụ DevOps này thường được kết hợp với các tập lệnh shell để đảm bảo rằng một nhánh đã hợp nhất trong GitHub được biến thành một tạo phẩm có thể triển khai

Bộ trang phục đẹp mắt đó chắc chắn được liên kết với nhau từ nhiều bộ phận riêng biệt

Giờ đây, áo hoodie của bạn có thể là vật che đậy phổ biến — bạn có thể mặc nó ở mọi nơi — giống như cách chiếc vỏ là thứ chứa nhiều công cụ phần mềm lại với nhau. Mặc dù nó phổ biến, nhưng nó cũng là một trong những vấn đề DevOps/TechOps định kỳ. Đó là một vấn đề vì chúng ta thường lạm dụng shell và coi nó như một ngôn ngữ lập trình

Dưới đây là một số mối quan tâm

  • Cú pháp có thể tối nghĩa. Tất cả chúng ta đều quen với nó, nhưng điều đó không làm cho nó tốt
  • Nó chậm. Mặc dù tốc độ của shell script hiếm khi quan trọng, nhưng việc cố gắng sử dụng shell như một ngôn ngữ lập trình sẽ lãng phí tài nguyên hệ thống
  • Chúng ta thường có thể bỏ qua các tính năng quan trọng của tập lệnh. Kiểm tra trạng thái của các chương trình sử dụng $?
  • Cấu trúc dữ liệu duy nhất của ngôn ngữ shell là chuỗi. Có nhiều cách để phân tách chuỗi thành từ. Chương trình expr có thể chuyển chuỗi thành số để làm phép tính số học. Chương trình ngày cho phép một số thao tác ngày, nhưng các quy tắc có thể không rõ ràng
  • Kiểm tra đơn vị không dễ dàng. Có một số gói như Bats có thể giúp kiểm tra đơn vị. Một ngôn ngữ như Python có khả năng kiểm tra đơn vị mạnh mẽ hơn nhiều

Nó không phải là vỏ bị hỏng. Shell không phải là một ngôn ngữ lập trình hoàn chỉnh. Nó không làm mọi thứ chúng ta cần

Điều gì đưa chúng ta đến với Bash-Bashing

Mục đích của bash-bashing là giảm sử dụng shell. Không có nhiều công việc thực tế, thật dễ dàng để thay thế các shell script bằng mã Python. Mã sửa đổi dễ đọc và dễ bảo trì hơn, chạy nhanh hơn một chút và có thể có bộ kiểm tra đơn vị phù hợp

Vì mã shell quá phổ biến nên tôi sẽ cung cấp một số ví dụ chi tiết về cách dịch các tập lệnh shell cũ sang Python. Tôi sẽ giả sử một chút quen thuộc với Python. Các ví dụ sẽ có trong Python 3. 6 và bao gồm các tính năng như pathlib và f-strings. Nếu bạn muốn làm theo, hãy xem xét việc tạo một môi trường ảo hoặc sử dụng conda

Các ví dụ về tập lệnh Shell là bash thuần túy và sẽ chạy ở bất kỳ đâu mà bash chạy. Các mẫu thiết kế áp dụng cho Windows;

Đó là một thế giới rộng lớn

Trước khi đi sâu vào mã, tôi muốn đặt một số ranh giới cho ví dụ. Thật hấp dẫn khi cung cấp các ví dụ về móc nối Git hoặc công việc Jenkins hoặc Triển khai mã đô thị

Tôi nghĩ những loại ví dụ này hơi quá cụ thể; . Tôi muốn phân biệt giữa trang phục thể thao thông thường và trang phục cụ thể mà chúng ta có thể mặc để leo núi. Ví dụ mã của tôi không được điều chỉnh cho một chuỗi công cụ DevOps cụ thể

Vỏ có thể được sử dụng cho nhiều thứ. Tôi sẽ sử dụng một ví dụ có một số loại thao tác tài nguyên cấp hệ điều hành phổ biến trong đó

  • Đọc tập tin cấu hình
  • Giết [và tạo] các quy trình
  • Làm phép tính ngày tháng
  • Tạo [và xóa] thư mục
  • Ứng dụng đang chạy
  • Quản lý tập tin

Ngoài ra còn có một số cân nhắc cấp cao hơn so với việc quản lý tài nguyên hệ điều hành. Những cân nhắc này bao gồm xử lý có điều kiện và lặp lại trên các đối tượng. Tôi sẽ đưa ra một ví dụ về việc lặp lại các tệp, nhưng các tập lệnh cũng lặp lại các quy trình hoặc thậm chí các dòng trong một tệp

Shell script thường quản lý tài nguyên mạng. Điều này có nghĩa là truy cập tài nguyên từ xa thông qua curl và wget. Sử dụng các chương trình này làm cho việc quản lý tài nguyên mạng giống như chạy ứng dụng và quản lý tệp cục bộ. Vì điều này, tôi sẽ không coi tài nguyên mạng là một loại đối tượng riêng biệt

Một kịch bản đại diện

Tôi có một tập lệnh shell nhỏ với một số ví dụ về các hoạt động tài nguyên khác nhau. Tập lệnh này thao tác một số tài nguyên hệ điều hành và chạy một ứng dụng bên ngoài. [Vâng, ứng dụng bí mật trong Python. ]

Kịch bản này dường như có bốn bước quan trọng

  1. Nó giết chết một quá trình. Điều này liên quan đến việc đọc một tệp để lấy id tiến trình, sau đó thực hiện các lệnh ps và kill. ví dụ. tệp pid được tạo bởi một số quy trình khác và được đọc bởi tập lệnh này. Nếu tệp không có bất kỳ nội dung nào, sẽ có một đường dẫn logic để xử lý nội dung đó. Nếu tập tin thậm chí không tồn tại thì sao?
  2. Nó tạo một thư mục có tên dựa trên ngày hiện tại. Thư mục này được sử dụng trong các bước tiếp theo. Nếu thư mục đã tồn tại, có một thông báo khó chịu. Đây có phải là mặt nạ một vấn đề?
  3. Nó chạy một ứng dụng phân tích. Có một vòng lặp khó hiểu chỉ xử lý một tệp từ danh sách tên tệp được sắp xếp. Tôi sẽ đi sâu vào các sắc thái của cấu trúc đó bên dưới. Tôi đã đưa nó vào làm đại diện cho các thuật toán có thể được đơn giản hóa khi sử dụng Python
  4. Cuối cùng, nó sao chép tệp đầu ra sang một vị trí thứ hai, được đặt tên là hiện tại. txt. Thao tác sao chép đủ điều kiện bằng câu lệnh if để kiểm tra xem một tệp có mới hơn hay không trước khi thay thế tệp khác

Tôi nhấn mạnh cụm từ “dường như có” bởi vì shell script có thể có những tác dụng phụ khó nhận biết. Ví dụ cụ thể này rõ ràng về các tệp và thư mục mà nó tạo ra và quá trình nó giết chết. Nói chung, ý tưởng ban đầu đằng sau trình bao là làm cho việc quản lý tài nguyên trở nên rõ ràng. Tuy nhiên, lý tưởng này không phải lúc nào cũng được đáp ứng trong thực tế

Bạn có thể thích áo hoodie của bạn. Nhưng nó có băng keo giữ nó lại với nhau

Một ví dụ về sự tối nghĩa của shell là cách thiết lập thư mục làm việc hiện tại. Lệnh cd đủ rõ ràng, nhưng với sự có mặt của các trình bao con sử dụng [], có thể gây khó khăn cho việc phân biệt một chồng các lệnh trình bao lồng nhau và cách thư mục làm việc thay đổi khi các trình bao con thoát ra

Hãy viết lại ví dụ này thành một cái gì đó có thể được kiểm tra đơn vị. Tôi sẽ bắt đầu từ đầu và đi sâu vào chi tiết

Bắt đầu từ trên cùng

Theo từ “trên cùng”, ý tôi là phần tóm tắt cấp cao về những gì tập lệnh thực hiện. Kịch bản này dường như có bốn bước. Đây là một số mã Python phản ánh tóm tắt tổng thể

Tôi đã lý tưởng hóa bốn bước thành các chức năng riêng biệt. Điều này để lại một khoảng trống để thực hiện từng chức năng. Tôi đã cố gắng tránh đưa ra quá nhiều giả định. Như một vấn đề thực tế, chúng ta thường có một bức tranh rõ ràng hơn về chức năng của shell script;

Từ tệp cấu hình đến quản lý quy trình

Một trong những tính năng thú vị hơn của shell là các biến có xu hướng toàn cầu. Tuy nhiên, có một số ngoại lệ và lưu ý dẫn đến các tập lệnh shell bị hỏng hoặc hoạt động không nhất quán. Điều này có nghĩa là các biến môi trường như PID_FILE và PID là đầu ra tiềm năng từ một bước và đầu vào tiềm năng cho bước sau. Nó hiếm khi rõ ràng

Một phần của quá trình viết lại có nghĩa là xác định các biến toàn cục được sử dụng trong các phần khác của tập lệnh. Điều này có thể khó khăn trong các kịch bản phức tạp. Trong ví dụ này, thật dễ dàng để kiểm tra mã để đảm bảo rằng hai biến này là cục bộ hiệu quả

Một phần khác của viết lại có nghĩa là xác định những thứ giống như tham số cấu hình hơn là các biến đơn giản. Những thứ như tên tệp và thư mục bằng chữ là những ứng cử viên rõ ràng để được coi là tham số cấu hình. Có các mục có thể định cấu hình khác như chuỗi tìm kiếm và mẫu ký tự đại diện cũng có thể cần được coi là một phần của cấu hình bên ngoài. Tôi khuyên bạn nên làm chậm với thông số hóa

Lời khuyên của tôi là hãy làm mọi việc trước. Tổng quát hóa chúng sau

Đây là cách triển khai hàm kill_ process[]

Tôi đã thay thế thành ngữ đọc PID và < được sử dụng để kết nối các tệp đang mở với chương trình sẽ được thực thi. Trong Python, tôi đã mở các tệp một cách rõ ràng. Sau đó, tôi có thể chuyển các kết nối tệp đang mở tới hàm check_call[] để được ánh xạ tới thiết bị xuất chuẩn của tiến trình con

Phép biến đổi này biến lệnh shell ngắn gọn của $APP $filename >”${output_dir}/summary_${name}. txt” thành một cái gì đó như thế này trong Python

Đầu tiên, tôi đã tạo tên tệp đích bằng Đường dẫn và toán tử /. Sau đó, tôi đã xây dựng lệnh dưới dạng danh sách các từ từ APP_NAME và đường dẫn sẽ được xử lý. Sau khi mở tệp mục tiêu, tôi đã chạy lệnh kết quả với đầu ra tiêu chuẩn hướng đến tệp đang mở

“Này, đợi một chút,” bạn có thể nói. “Bạn chỉ đang chạy một chương trình Python từ một tập lệnh Python khác. ”

Điểm tốt. Bạn đã xác định cấp viết lại tập lệnh tiếp theo. Chúng tôi có thể hợp nhất các bước này thành một ứng dụng tổng hợp nhập ứng dụng module_design_analytics ban đầu. Cũng có thể hợp lý khi sử dụng mô-đun runpy để chạy ứng dụng từ bên trong tập lệnh này. Đây là những tối ưu hóa tiềm năng quan trọng

Kiểm tra thời gian sửa đổi và sao chép tệp

Bước cuối cùng sẽ sao chép một tệp nếu nó mới hơn một số tệp mục tiêu

Shell sử dụng [ file -nt file ] để so sánh thời gian sửa đổi của hai tệp. Tôi đã thay thế thao tác này bằng một thao tác rất rõ ràng để lấy hệ điều hành. cấu trúc stat[] cho mỗi tệp. Sau đó, tôi so sánh thời gian sửa đổi, được gọi là st_mtime trong mỗi cấu trúc trạng thái hệ điều hành

Việc sao chép tệp được thực hiện với mô-đun Shutil. Cái này có chức năng copy2[] để sao chép dữ liệu cũng như siêu dữ liệu. Có các lựa chọn thay thế cho điều này trong mô-đun Shutil cung cấp nhiều quyền kiểm soát hơn đối với cách xử lý siêu dữ liệu

nhập khẩu

Đây là danh sách các mô-đun được nhập bởi tập lệnh này

Trong một số trường hợp [tôi. e. , datetime, shutdown], tôi đã sử dụng toàn bộ mô-đun. Trong các trường hợp khác [tôi. e. , pathlib, sub process], tôi chỉ nhập lớp, chức năng hoặc ngoại lệ cụ thể cần thiết. Tôi đã làm điều này để hiển thị cả hai phong cách. Khi một chức năng như check_call[] được nhập, thì tên đó không yêu cầu bất kỳ bằng cấp nào. Khi một mô-đun như Shutil được nhập thì hàm này sẽ yêu cầu mô-đun đó làm tiêu chuẩn không gian tên rõ ràng. đóng cửa. sao chép2[]. Có những lý do tốt cho mỗi phong cách

psutil không phải là một phần của thư viện chuẩn. Nó sẽ phải được cài đặt riêng

thử nghiệm

Khi có một số - được cho là - mã đang hoạt động, bước tiếp theo là tạo các bài kiểm tra đơn vị. Đó là một thực hành tốt để phát triển ổ đĩa thử nghiệm. Lý tưởng nhất là chúng ta sẽ viết các bài kiểm tra đơn vị cho bốn bước chức năng cấp cao trong tập lệnh shell. Lý tưởng đó khó đạt được khi chúng ta không chắc mình biết mọi thứ mà một bước nhất định làm có liên quan đến các bước tiếp theo. Có rất nhiều trạng thái chung, toàn cầu cần được hiểu

Nếu chúng tôi đang sử dụng Python thay vì trình bao, thì chúng tôi có lợi thế là bắt đầu lại từ đầu. Chúng ta có thể sử dụng các kỹ thuật phát triển thử nghiệm đầu tiên

Trong cả hai trường hợp, điều cần thiết là giả lập các tài nguyên cấp hệ điều hành mà các ứng dụng Python của chúng tôi sẽ chạm vào. Đối với điều này, tôi đã sử dụng unittest. thư viện giả. Để xây dựng các đối tượng giả thích hợp, cần xem mã. Một số chuyên gia kiểm thử gọi đây là kiểm thử “hộp trắng” vì tôi đang điều chỉnh các đối tượng giả cho phù hợp với phần mềm đang kiểm thử

Đây là một thử nghiệm đại diện cho một trong các chức năng của tập lệnh của chúng tôi. Điều này sẽ kiểm tra hàm copy_to_current[] cho một mối quan hệ về thời gian sửa đổi

Thử nghiệm nhập tập lệnh để thử nghiệm; . py. Tôi đã áp dụng hai bản vá cho không gian tên nơi tập lệnh thực thi

  • Việc nhập Shutil được thay thế bằng một đối tượng Mock
  • Việc nhập pathlib. Đường dẫn cũng được thay thế bằng một đối tượng Mock

Cả hai đối tượng giả này đều trở thành đối số cho hàm kiểm tra để tôi có thể sử dụng chúng. Khi có nhiều hơn một trình trang trí @patch, hãy lưu ý thứ tự từ trong ra ngoài của tên tham số. Đây là hệ quả của các quy tắc áp dụng trình trang trí của Python

Tôi cũng đã tạo một mô hình giả cho đối số result_path và gán nó cho biến mock_result_path. Trong trường hợp này, tôi không vá tên đã nhập, tôi đang cung cấp đối số cho một hàm

Chức năng được kiểm tra sử dụng mã như thế này

Để chắc chắn điều này sẽ hoạt động, tôi đã tạo một thuộc tính stat. Nó là một Mock với return_value. Giá trị được trả về là Mô phỏng có thuộc tính st_mtime. Tôi đã chế nhạo hành vi được sử dụng bởi đoạn mã đang thử nghiệm

Đối số mock_path là đối số thay thế cho định nghĩa lớp Đường dẫn. Chức năng được kiểm tra sẽ sử dụng mã như thế này

Chúng tôi hy vọng rằng Đường dẫn giả sẽ được đánh giá một lần để tạo đối tượng Đường dẫn. Hành vi mặc định của lệnh gọi hàm là tạo một đối tượng Mock. Một bài kiểm tra có thể tham chiếu mô hình đã tạo với mock_path. return_value. Dựa trên mã được kiểm tra, đối tượng Mock được trả về phải có hàm stat[] và kết quả của hàm đó phải có thuộc tính st_mtime

Khi đánh giá kịch bản. copy_to_current[] trong bối cảnh thử nghiệm này, tôi có rất nhiều kỳ vọng. Cái quan trọng nhất là cái cửa đó. copy2 sẽ được gọi. Tôi chính thức hóa kỳ vọng này như sau

Tôi cũng quan tâm đến việc xác nhận phương thức stat[] đã được gọi cho mỗi đối số. Tôi có thể thêm điều này như một lời khẳng định

Toàn bộ vấn đề ở đây là thêm các bài kiểm tra với các mối quan hệ dấu thời gian khác nhau để kiểm tra các hành vi khác. các py. gói thử nghiệm có thể tìm và chạy các thử nghiệm này vì chúng có mẫu tên nhất quán

Bước tiếp theo

Tất nhiên là chưa xong. Tôi chưa thực sự đề cập đến việc ghi nhật ký hoặc cấu hình. Tập lệnh này có sử dụng các biến môi trường bên ngoài không?

Một số shell script sử dụng lệnh getopt để phân tích các tùy chọn. Các tập lệnh Shell khác sử dụng $0, $1, v.v. , để thu thập các đối số vị trí. Rất nhiều tính năng trong số này có thể bị ẩn bên trong một tập lệnh, gây khó khăn cho việc phát hiện ra tập lệnh thực sự làm gì và dự kiến ​​sử dụng nó như thế nào

Hơn nữa, có thể có một vòng tối ưu hóa khác. Như đã lưu ý ở trên, tập lệnh này chạy chương trình Python. Có thể thích hợp để kết hợp ứng dụng và tập lệnh thành một ứng dụng tổng hợp

Sự kết luận

Shell là một môi trường lập trình nguyên thủy. Nó hoạt động tốt, nhưng nó không phải là một ngôn ngữ quá phức tạp. Tôi nghĩ nó giống như một bộ đồ giải trí của những năm 1970, có lẽ được làm bằng polyester dệt kim đôi, với đường khâu trên cùng tương phản. Bạn biết đấy - đã hẹn hò. Và chắc chắn không phù hợp để leo núi hoặc một ngày ở bãi biển

Hai vấn đề lớn nhất là sự ít cấu trúc dữ liệu hữu ích và khó kiểm tra đơn vị

Chúng ta không cần phải loại bỏ tất cả việc sử dụng vỏ. Nhưng chúng ta có thể hưởng lợi từ việc giảm sử dụng trình bao cho một số thứ mà nó đặc biệt giỏi. Tôi là người thích chuyển các tập lệnh shell thành một vài dòng mã đặt biến môi trường, thiết lập thư mục làm việc hiện tại và chạy chương trình đích

Những gì chúng tôi muốn làm là thay thế các tập lệnh shell phức tạp bằng mã được viết bằng ngôn ngữ lập trình cung cấp cho chúng tôi một họ cấu trúc dữ liệu phong phú. Trong nhiều trường hợp, chúng ta có thể tìm thấy các khối mã shell lớn đang thực hiện các phép tính vốn là một tính năng đơn giản, tích hợp sẵn của một ngôn ngữ lập trình như Python

Chuyển sang Python có nghĩa là chúng ta có thể tận dụng các cấu trúc dữ liệu thông minh hơn. Chúng tôi có thể làm việc với các số và ngày theo cách không phải xử lý chuỗi. Phần tốt nhất? . Một thay đổi nhỏ đối với tập lệnh shell không phá vỡ mọi thứ. Thay vào đó, nó được kiểm tra giống như phần còn lại của ứng dụng. Chúng tôi có thể đưa nó vào sản xuất với rất nhiều niềm tin rằng nó sẽ thực sự hoạt động

BẢN TƯỜNG TRÌNH. Những ý kiến ​​này là của tác giả. Trừ khi có ghi chú khác trong bài đăng này, Capital One không liên kết với, cũng như không được xác nhận bởi bất kỳ công ty nào được đề cập. Tất cả các nhãn hiệu và tài sản trí tuệ khác được sử dụng hoặc hiển thị là quyền sở hữu của chủ sở hữu tương ứng của họ. Bài viết này là © 2017 Capital One

Chủ Đề