New FAMILUG

Friday, 6 November 2020

bash, shopt và =, **

bash là UNIX shell được dùng nhiều nhất hành tinh này. bash có ở khắp mọi hệ điều hành Linux based, hay kể cả OSX/MacOS cho tới tháng 10 năm 2019 (khi Apple quyết định thay zsh làm shell mặc định trên MacOS)

Sự thay đổi này của Apple khiến cho bạn nếu vẫn "chày cối" dùng bash sẽ gặp phải một số bất lợi nhất định (tất nhiên là khắc phục được). Trên MacOS, phiên bản của bash có sẵn rất cũ, vẫn là bash 3.X. Trong khi Ubuntu 18.04 đã dùng 4.4, còn Ubuntu 20.04 thì dùng hẳn bash 5.0. 

 

Photo by Clement Chai on Unsplash

Chuyện này hoàn toàn không phải vấn đề nếu người dùng macOS chủ động cài phiên bản bash mới nhất bằng brew. Cho đến một ngày có người hỏi: câu lệnh ls nào trên bash để liệt kê ra tất cả file .pyc trong tất cả các thư mục con hiện tại?
Việc này hoàn toàn làm được với `find . -name '*.pyc' -type f`, nhưng với ls thì??? 

glob là gì 

Trên các shell, dấu * được gọi là "glob", glob thay cho "bất cứ string nào", ví dụ muốn liệt kê các file .py trong thư mục hiện tại, gõ:

ls *.py
Kết quả sẽ in ra mọi file có đuôi .py trong thư mục.
ls /etc/*.conf

sẽ in ra tất cả các tên file có đuôi .conf trong thư mục /etc/ 

globstar là gì? recursive glob là gì?

globstar là tính năng chỉ có từ bản bash 4.0 trở đi, vậy nên nếu bạn thuộc hội "sysadmin" đã có tuổi, lại không theo sát đọc các tính năng khi bash ra bản 4, thì hoàn toàn không biết tính năng này.

> The new ** globbing operator matches filenames and directories recursively.
globstar If set, the pattern ** used in a pathname expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a /, only directories and subdirectories match.

sau khi bật tính năng globstar bằng lệnh shopt -s globstar câu lệnh sau sẽ liệt kê tất cả các file có tên .py trong thư mục hiện tại, bao gồm cả thư mục con:

vagrant@buster:~$ mkdir -p a/b/c/d/
vagrant@buster:~$ touch a/b/c/d/hocpython.py
vagrant@buster:~$ cd a
vagrant@buster:~/a$ ls **/*.py
ls: cannot access '**/*.py': No such file or directory
vagrant@buster:~/a$ shopt -s globstar
vagrant@buster:~/a$ ls **/*.py
b/c/d/hocpython.py
vagrant@buster:~/a$ echo $BASH_VERSION
5.0.3(1)-release

zsh 5.4.2 mặc định hỗ trợ tính năng này, không cần bật gì, dưới cái tên "Recursive Globbing" (trong man 1 zshexpn)

shopt là gì? 

shopt là một câu lệnh built-in của bash, dùng để bật tắt các tính năng ít được dùng tới 

$ type shopt
shopt is a shell builtin

$ man 1 bash

...

shopt [-pqsu] [-o] [optname ...]
              Toggle the values of settings controlling optional shell behavior.


Liệt kê các tính năng đang bật:

$ shopt -p | grep -- -s
shopt -s checkwinsize
shopt -s cmdhist
shopt -s complete_fullquote
shopt -s expand_aliases
shopt -s extglob
shopt -s extquote
shopt -s force_fignore
shopt -s globasciiranges
shopt -s globstar
shopt -s histappend
shopt -s interactive_comments
shopt -s login_shell
shopt -s progcomp
shopt -s promptvars
shopt -s sourcepath

Các tính năng đang tắt:

$ shopt -p | grep -- -u | head
shopt -u autocd
shopt -u assoc_expand_once
shopt -u cdable_vars
shopt -u cdspell
shopt -u checkhash
shopt -u checkjobs
shopt -u compat31

...

 

Bonus: so sánh string = trong test trên shell 

Có lẽ hơi ngại khi thừa nhận rằng sau 10 năm dùng bash, mình luôn nghĩ so sánh string khi test dùng == như Python. Tất nhiên code vẫn chạy, do bash hỗ trợ 2 cú pháp: = và ==.

       string1 == string2
       string1 = string2
              True  if the strings are equal.  = should be used with the test command for POSIX conformance.  When used with the [[
              command, this performs pattern matching as described above (Compound Commands).
Ví dụ:

x="PyMI.vn"

if [ "$x" == "PyMI.vn" ]; then

    echo Python

fi

Thì cùng có thể viết 

x="PyMI.vn"

if [ "$x" = "PyMI.vn" ]; then

    echo Python

fi

Thậm chí đoạn code sau còn chạy trên nhiều shell khác nữa do nó "chuẩn POSIX".

Hết.

HVN at "học python tại PyMi" https://pymi.vn and https://www.familug.org 

1 comment: