Bash vs Z shell

Bash vs Z shell
Photo by julia roman from Pixabay

Từ phiên bản MacOS Catalina, Apple đã chuyển sang dùng shell mặc định là zsh thay cho bash. Tôi thì vốn quen dùng bash nên đã từng rất lo, sợ rằng những gì tôi biết trước giờ không còn dùng được nữa. Rất may là chuyện đó không xảy ra.

Giờ đây thì tôi biết được, nguyên nhân của việc này liên quan đến giấy phép (license) của bash. Các phiên bản mới hơn của bash được cấp phép theo giấy phép GPLv3, và Apple không thể sử dụng phần mềm với giấy phép này (nguyên nhân cụ thể không rõ, nhưng có vẻ nó yêu cầu Apple phải công khai mã nguồn của những phần mình sửa đổi bash).

Vì vậy, bash được cài đặt sẵn trong macOS có phiên bản rất cũ (3.2.57) – đây là phiên bản cuối cùng của bash sử dụng giấy phép GPLv2 mà Apple được phép sử dụng phần mềm theo giấy phép này.

Một “phần mềm” nổi tiếng là hạt nhân Linux cũng cấp phép người dùng theo giấy phép GPLv2 mà không sử dụng GPLv3.

Nhưng trong quá trình tìm hiểu, tôi có đọc được nhiều bài viết, đại ý là zsh là nhất, zsh tuyệt vời, zsh vô đối. nhưng đọc xong vẫn chưa hiểu vô đối ở chỗ nào. Giờ hiểu được phần nào rồi nên tôi tổng hợp lại, để bạn nào cũng gặp khó khăn như tôi có thể tham khảo. Do chỉ có kinh nghiệm với bash, nên trong bài viết này tôi sẽ so sánh zsh và bash, xem sự khác biệt là gì, và liệu zsh có vô đối thật không.

Shell và Terminal

Shell (thuật ngữ này chỉ tồn tại trên các hệ điều hành họ Unix và Linux) là các chương trình nhận lệnh từ người dùng và gửi vào nhân của hệ điều hành để xử lý. Không chỉ vậy, các dòng lệnh cũng được coi là một ngôn ngữ lập trình, và đã được chuẩn hóa trong tiêu chuẩn POSIX. Bởi vậy, người dùng có thể dùng shell script để tự động hóa một số công việc.

Các bản phân phối Linux đến bây giờ vẫn duy trì giao diện dòng lệnh (CLI), cho phép bạn mở máy tính và làm việc trực tiếp với shell không cần một phần mềm trung gian nào khác (bằng cách ấn Ctrl+Alt+F1-F6 hoặc F8). Tuy nhiên, hệ điều hành nào cũng phát triển giao diện đồ (GUI) họa hết rồi, tôi dùng MacOS còn không có giao diện dòng lệnh luôn. Và trong giao diện đồ họa, nếu muốn làm việc với shell, chúng ta cần đến một phần mềm khác, gọi là Terminal.

Terminal nếu đúng nghĩa gốc ban đầu là từ để chỉ các thiết bị đầu cuối, ví dụ VT52 (DEC), được sử dụng phổ biến trước đây. Đây không phải là máy tính mà là thiết bị đảm nhiệm đầu vào và đầu ra của máy tính (thường chỉ có bàn phím và màn hình, có khi thay màn hình bằng máy in, không có bộ xử lý). Những thiết bị này được sử dụng rộng rãi vào thời máy tính cá nhân còn chưa được phổ cập, mà máy tính ở đây là các máy mainframe to như cái nhà.

Ngày nay thì không còn ai sử dụng những thiết bị như vậy nữa. Mà từ Terminal được chuyển đổi nghĩa, dùng để chỉ các phần mềm mô phỏng các thiết bị đầu cuối ngày xưa. Nó là những chương trình cho phép chúng ta tương tác với shell (nhưng bản thân nó không phải là shell nhé 😁).

Có rất nhiều chương trình khác nhau có chức năng như vậy, phần lớn đều để tiêu đề là Terminal, nhưng trước giờ tôi mới chỉ sử dụng gnome-terminal (là Terminal được cài mặc định trên Ubuntu, cho bạn nào thắc mắc về cái tên này 😄) và Terminal mặc định của MacOS mà thôi. Trên Linux thì tôi thấy giao diện CLI đẹp hơn Terminal đó, nên bạn nào muốn try hard có thể dùng thử.

Lịch sử của bash và zsh

Lịch sử của shell liên quan đến lịch sử của Unix và Linux, thực sự thì rất phức tạp. Tôi cũng mới chỉ sử dụng bash và zsh nên trong bài viết này chỉ tập trung vào bash và zsh mà thôi.

Bourne-again shell (bash) là shell được phát triển năm 1989 bởi Brian Fox. Nó là Unix shell được phát triển để thay thế cho Bourne shell (sh).

Nói qua một chút về sh, thì nó là shell cổ điển nhất (hay đúng hơn là shell tương thích với chuẩn POSIX đầu tiên, những shell trước đó do không tương thích nên giờ không ai nhắc đến nữa), được cài đặt kèm với hệ điều Unix V7. Lúc bấy giờ (và đến tận bây giờ) Unix là hệ điều hành thương mại và người ta phải trả tiền mới được sử dụng.

Rất may là Unix có một thiết kế tuyệt vời, nó cho phép người sử dụng có thể tự phát triển các thành phần để thay thế các thành phần gốc (nhằm giảm chi phí). Và bash là một thành phần như thế.

Hiện nay nhiều hệ điều hành vẫn có sh trong hệ thống (/bin/sh) tuy nhiên nó không phải là Bourne shell gốc. Trên MacOS thì nó là bản sh của freeBSD, còn trên Ubuntu trước đây nó thường là symbolic link đến bash, gần đây thì nó là symbolic link đến dash.

Bash nhanh chóng được chấp nhận và được sử dụng rộng rãi trong các hệ điều hành Linux và cả MacOS. So với sh, bash có nhiều tính năng nâng cao hơn, đặc biệt là nó có thể được sử dụng để lập trình (shell script) tốt hơn sh. Người dùng có thể lập trình những đoạn code nhỏ để tự động hóa công việc.

Z shell (zsh) được phát triển vào năm 1990 bởi Paul Falstad. Nó cũng là một Unix shell dựa trên sh. Tương tự như bash, zsh cũng có nhiều tính năng nâng cao hơn sh (và rất nhiều trong số đó cũng có ở bash). Zsh cũng có thể được sử dụng để shell script, và đặc biệt, hiện tại nó có một cộng đồng sử dụng và hỗ trợ rất mạnh.

Ngoài bash và zsh thì còn nhiều shell khác đang tồn tại, ví dụ csh, tcsh, ksh. Nhưng tôi chưa dùng cái nào cả nên không có ý kiến gì.

Bash vs. zsh

Trong quá trình tìm hiểu, tôi có đọc được bài viết này, so sánh rất chi tiết sự khác biệt của zsh và bash. Với người dùng bình thường như tôi thì không cần phải hiểu sâu tới mức như thế. Nhưng đó là một sự so sánh rất chi tiết và cho thấy rõ sự khác biệt của hai shell.

Trong bài viết này tôi không có nhu cầu trình bày những kiến thức đó, và tôi cũng không đủ kiến thức để làm việc đó. Tôi sẽ trình bày những gì tôi cảm nhận được, với tư cách là một người dùng, vẫn thường dùng các câu lệnh khi làm việc.

Về cơ bản, tôi thấy bash và zsh khá giống nhau. Tôi chuyển từ bash sang zsh hầu như không gặp vấn đề gì cả. Phần lớn các lệnh bash mà tôi biết vẫn hoạt động tốt trên zsh (trừ một số lệnh built-in quá đặc thù như read). Trong những phần dưới đây, tôi sẽ so sánh cụ thể hơn.

Auto completion

Chỉ nói auto completion, tức là tự động hoàn thiện lệnh, một cách chung chung thì cũng khó so sánh. Trong phần này tôi sẽ lấy ví dụ cụ thể là lệnh cd. Đây là một lệnh khá cơ bản được dùng bởi cả bash và zsh để thay đổi thư mục làm việc hiện tại.

Việc gõ lệnh kèm với tên thư mục để di chuyển thì đơn giản rồi, tôi không có gì để so sánh. Ở đây, cái tôi muốn nói là khả năng tự động tìm kiếm các thư mục hiện có.

Cả bash và zsh đều cung cấp cũng phương thức khác nhau để tự động hoàn thiện lệnh (chủ yếu là tự động tìm kiếm tên thư mục). Cách tự động hoàn thiện của hai shell này lại có chút khác biệt.

Với bash thì chúng ta có thể gõ một vài ký tự đầu rồi ấn tab. Nếu những ký tự đó khớp với một thư mục duy nhất thì bash sẽ tự hoàn thiện cho chúng ta. Nếu không thì chúng ta phải ấn tab thêm 1 lần nữa thì bash mới đưa ra những gợi ý thư mục khớp với những gì chúng ta nhập vào.

Nhưng bash chỉ gợi ý để người dùng tiếp tục gõ tên thư mục cho đúng mà thôi. Ngoài ra, bash gợi ý tất cả file và thư mục khớp với từ khóa, nên nhiều khi nó khá là thừa và thư mục mà chúng ta cần có thể không được hiển thị vì danh sách quá dài.

$ cd Do <tab> <tab>
Documents/ Downloads/

Với zsh thì mọi thứ dễ dàng hơn khá nhiều. Nếu bạn gõ cd Do <tab> thì zsh sẽ tìm tất cả các thư mục khớp mới những ký tự đó (bắt đầu bằng Do) và hiển thị cho người dùng lựa chọn bằng cách tiếp tục ấn tab.

% cd Do <tab>
Documents/ Downloads/

Lúc này, giả sử bạn chọn Downloads thì tên thư mục này sẽ tự động được điền vào lệnh để hoàn thiện. Và nếu tiếp tục ấn tab, zsh sẽ đưa ra các gợi ý tiếp theo là các thư mục con của Downloads. Zsh đưa ra gợi ý khá chính xác, khi chỉ đưa thư mục mà thôi, còn file thì không được gợi ý.

Như vậy khả năng tự động hoàn thiện lệnh thì cả bash và zsh đều có. Tuy nhiên tôi thấy zsh hoạt động tốt hơn, và tương tác của zsh với người dùng cũng tốt hơn. Không chỉ cd và hầu như tất cả các lệnh đều như vậy.

Hơi ngoài lề một chút, nhưng zsh có thể bỏ luôn cd và chỉ gõ tên thư mục là cũng chuyển sang thư mục đó. Trong khi với bash đây là cú pháp lỗi (do gọi một đường dẫn không kèm lệnh gì là cú pháp để thực thi một file, và nếu chỉ gọi thư mục thì sẽ lỗi).

Sửa lỗi chính tả

Khả năng phát hiện và sửa lỗi chính tả là một tính năng khá hay và hữu ích với người dùng, đặc biệt là với những người dùng học tiếng Anh như một ngoại ngữ. Cá nhân tôi cũng thỉnh thoảng gõ sai lệnh và mong muốn các shell có thể phát hiện và tự động sửa giúp tôi.

Cả bash và zsh đều có tính năng này. Tuy nhiên, hai shell lại có cách hoạt động rất khác nhau. Chúng ta sẽ xem xét một ví dụ cụ thể ở dưới đây:

Ví dụ tôi muốn chuyển đến thư mục Downloads nhưng lại gõ nhầm thành Downlaods (giả sử thế thôi chứ bình thường gõ tab là tên thư mục được gợi ý rồi 👍). Mặc định thì bash sẽ không bật chế độ kiểm tra và tự động sửa lỗi chính tả. Và với một tên gõ sai thì thường kết quả sẽ thế này:

$ cd Downlaods
bash: cd: Downlaods: No such file or directory

Để bật tính tính năng tự động sửa lỗi chính tả thì chúng ta cần dùng lệnh sau:

$ shopt -s cdspell

Sau khi bật kiểm tra và sửa lỗi chính tả, thì nếu gõ sai chúng ta sẽ nhận được kết quả thế này (tên thư mục tự động được sửa và thực thi lệnh):

$ cd Downlaods
Downloads

zsh cũng không được bật chế độ kiểm tra chính tả mặc định. Nên nếu bạn gõ sai thì kết quả nhận được vẫn là một thông báo lỗi mà thôi. Để bật chế độ kiểm tra và tự động sửa lỗi chính tả của zsh cần dùng lệnh sau:

% setopt correct_all

Sau khi bật lên, thì zsh cũng thực hiện kiểm tra chính tả, tuy nhiên nó không tự động sửa lỗi mà xác nhận lại với người dùng:

% cd Downlaods
zsh: correct 'Downlaods' to 'Downloads' [nyae]?

Câu thông báo về lỗi sai là mặc định và [nyae] có nghĩa là no, yes, abort, edit. Thông báo này hoàn toàn có thể thay đổi được (bằng cách thay đổi biến môi trường SPROMPT). Ví dụ:

% export SPROMPT="Correct %R to %r? [Yes, No, Abort, Edit] "

Sau khi có thông báo lỗi, nếu chọn y thì lệnh sẽ tự động được sửa và thực thi. Không chỉ kiểm tra tên thư mục, zsh còn có thể kiểm tra cả câu lệnh có sai chính tả hay không. Đây là điểm zsh nổi trội hơn bash và cũng là tính năng rất cần thiết.

Ví dụ chúng ta gõ một lệnh sai như sau:

% ehco "Hello"
zsh: correct 'ehco' to 'echo' [nyae]? y
Hello

Giao diện, theme và plugin

Cả hai shell bash và zsh đều rất xấu nếu chỉ dùng mặc định, và cả hai cũng đều cho phép thay đổi giao diện bằng cách sử dụng theme và plugin.

Bằng cách clone và cài đặt bash-it template, chúng ta có thể sử dụng theme cho bash:

$ git clone --depth=1 https://github.com/Bash-it/bash-it.git ~/.bash_it
$ ~/.bash_it/install.sh

Theme bobby là theme được cài đặt mặc định của bash-it. Ngoài ra còn rất nhiều theme khác có sẵn cho bash-it và người dùng có thể dễ dàng lựa chọn bằng cách thay giá trị của biến môi trường BASH_IT_THEME.

bobby
Nguồn: Bash-it

Còn với zsh, chúng ta có một bộ công cụ gọi là Oh My Zsh để quản lý theme và plugin. Oh My Zsh là một platform được phát triển bởi cộng đồng người dùng zsh. Đây là công cụ cổ điển nhất và cũng phổ biến nhất để quản lý và cấu hình zsh.

Oh My Zsh là một nơi tốt để làm việc với zsh khi có hơn 250 plugin và 140 theme tích hợp sẵn (và vẫn tiếp tục được update). Nó cho phép người dùng làm việc với zsh một cách thoải mái hơn, dễ dàng cá nhân hóa hơn và có thể truy cập đến những tính năng nâng cao hơn, vượt ra ngoài thiết kế ban đầu của zsh.

Cài đặt Oh My Zsh thì rất đơn giản (do đã có shell script để tự động hóa), chỉ cần chạy lệnh sau:

% sh -c "$(curl -fsSL
https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

Theme robbyrussell là theme mặc định, ngoài ra người dùng có thể chọn rất nhiều theme khác nhau và thay đổi biến môi trường ZSH_THEME là được:

robbyrussell
Nguồn: ohmyzsh

Ngoài ra, một tính năng khá hay của zsh (tích hợp sẵn, không cần cài thêm gì cả) gọi right handed side prompt (thiết lập thông qua biến môi trường RPROMPT. Khi sử dụng chế độ này, khi một lệnh quá dài, tràn tới phần bên phải thì phần này sẽ tự động được ẩn đi, nhường chỗ để hiển thị lệnh. Đây cũng là một điểm thú vị của zsh.

Sử dụng ký tự đại diện

Sử dụng ký tự đại diện được hỗ trợ rất tốt bởi bash. Chúng ta có thể sử dụng các ký tự đại diện (như *) theo nhiều cách khác nhau với bash. Nhưng zsh mặc định lại không hỗ trợ điều đó.

Ví dụ, chúng ta muốn hiển thị các file có đuôi .log trong thư mục hiện tại. Chúng ta có thể dùng lệnh echo như sau:

$ files="*.log"
$ echo $files
<danh sách các file có đuôi .log>

Tuy nhiên nếu dùng lệnh trên với zsh thì thay vì in ra các file có đuôi .log thì kết quả chúng ta nhận được là chuỗi ký tự *.log mà thôi (tuy nhiên nếu dùng lệnh echo *.log thì sẽ ra kết quả giống bash).

% file="*.log"
% echo $files
*.log

Để hỗ trợ kiểu cú pháp với ký tự đại diện, chúng ta phải thêm thiết lập sau cho zsh:

% set -o GLOB_SUBST

Giờ đây thì zsh cũng có thể dùng ký tự đại diện giống như bash.

Khai triển biến

Bash và zsh có cách khai triển biến khác nhau. Sự khác biệt rõ nhất được thể hiện khi khai triển các biến mà giá trị của chúng có chứa dấu cách.

Ví dụ bash khai triển biến như sau:

$ var="ls -l"
$ $var
total 160
...

Còn với zsh, kết quả hoàn toàn khác:

% var="ls -l"
% $var
zsh: command not found: ls -l

Đó là bởi khi, khi zsh khai triển biến, $var cũng tương tự như "$var" vậy. Còn với bash thì $var không phải là "$var". Sự khác biệt cũng được thể hiện trong nhiều tình huống khác nhau. Ví dụ:

Bash

$ var="foo bar"
$ [ $var = "foo bar" ]; echo $?
bash: [: too many arguments
2
$ [ "$var" = "foo bar" ]; echo $?
0

Zsh

% var="foo bar"
% [ $var = "foo bar" ]; echo $?
0
% [ "$var" = "foo bar" ]; echo $?
0

Kết luận

Bash và zsh đều là những shell có rất nhiều tính năng thú vị và hữu ích với người dùng. Hy vọng bài viết cung cấp được phần nào thông tin cho các bạn đang phân vân giữa bash hay zsh. Cá nhân tôi thì từ khi chuyển sang zsh cảm thấy rất thích và không thấy có nhu cầu quay lại bash nữa 👍.

Tôi xin lỗi nếu bài viết có bất kỳ typo nào. Nếu bạn nhận thấy điều gì bất thường, xin hãy cho tôi biết.

Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.