Wednesday, 15 October 2014

[bash] shell grammar (phần 1)

Theo quan sát của mình, hầu như mọi người học các ngôn ngữ lập trình đều theo cách rất cơ bản, học đầy đủ từ đầu đến cuối, sách này sách kia, nhưng với bash hầu hết người ta lại không học theo lối đó. Vì vậy có không ít điều rất đỗi bình thường của bash mà người dùng nhiều năm cũng không biết.

Chuỗi bài viết này tổng hợp đầy đủ (hay ít nhất là theo man 1 bash là thế) về "cú pháp" "NGỮ PHÁP" trong bash.

Bash gồm có:
- câu lệnh đơn (simple command)
- pipeline
- list
- các câu lệnh ghép (compound command)
- coprocess
- function definition

1. simple command
là một câu lệnh bình thường, ví dụ:
$ls -la
$ rm -r /tmp/pigmovie 2>&1 > /dev/null
TỪ KHOÁ:
exit status: giá trị một chương trình / câu lệnh trả về khi chạy trong shell. Là một số nguyên, với giá trị 0 biểu diễn cho câu lệnh chạy thành công, các giá trị khác (có thể âm) biểu diễn một lỗi nào đó đã diễn ra. Bash lưu exit status của câu lệnh cuối cùng nó chạy ở biến $?, có thể echo ra để xem:

$ mkdir /tmp/pikachu ; echo $?
0 # chạy thành công, tạo được thư mục /tmp/pikachu
$ mkdir /tmp/pikachu ; echo $?
mkdir: /tmp/pikachu: File exists
1 # chạy không thành công vì thư mục đã tồn tại, do đã tạo từ câu lệnh trước.
2. pipeline /ˈpʌɪplʌɪn/
là chuỗi một hay nhiều câu lệnh phân cách bởi dấu ``|`` hoặc ``|&``

TỪ KHOÁ:
stdin/stdout/stderr: Mỗi chương trình / câu lệnh khi chạy đều có:
  • stdin (đầu vào - biểu diễn bằng số 0)
  • stdout (đầu ra - biểu diễn bằng số 1)
  • stderr (đầu ra của các lỗi - biểu diễn bằng số 2)
Dấu ``|`` (pipe), sẽ kết nối stdout của câu lệnh trước nó vào stdin của câu lệnh theo sau nó.
Dấu ``|&`` là cách viết ngắn gọn của ``2>&1 |``,  nó kết nối stderr vào stdout của câu lệnh trước nó, rồi chuyển vào stdin của câu lệnh theo sau nó.

Viết 1 file python đơn giản ghi cả ra stdout và stderr với 2 nội dung khác nhau
hvn@XXX: /tmp () $ cat both.py
import sys
sys.stdout.write('meomeo')
sys.stderr.write('error goes')
chạy file này và xem nó ghi stderr, stdout ra các file riêng:
$ python both.py 2> err > out ; cat err; echo OUT:; cat out
error goesOUT:
meomeo
Xem sự khác biệt giữa ``|`` và ``|&``
 $ python both.py |& grep -c error
1 # grep -c đếm số dòng xuất hiện từ khoá được grep
 $ python both.py | grep -c error
error goes0 # vẫn nhìn thấy error goes vì nó đã ghi ra stderr

3. list
một list là một chuỗi của một hay nhiều pipeline phân cách bởi dấu ; &, && hay ||
TỪ KHOÁ:
control operators: trong bash có các operator &, &&, ||

AND list:
command1 && command2
command2 chỉ được chạy khi command1 trả về exit status = 0 (tức chạy thành công). VD:
$ ls /etc/passwd && echo file /etc/passwd ton tai
/etc/passwd
file /etc/passwd ton tai
OR list
command1 || command2
command2 chỉ chạy khi command1 trả về exit status khác 0 (chạy không thành công, gặp lỗi). VD:
$ ls /tmp/xyz || echo /tmp/xyz khong ton tai
ls: /tmp/xyz: No such file or directory
/tmp/xyz khong ton tai
khi thêm & ở cuối một command, command đó sẽ chạy ở chế độ background trong 1 subshell và luôn trả về exit status = 0, shell hiện tại sẽ KHÔNG đợi câu lệnh này chạy xong. Ví dụ:
$ date; sleep 10 & date
Wed Oct 15 00:20:29 ICT 2014
[3] 5438 # pid của process chạy lệnh sleep 10
Wed Oct 15 00:20:29 ICT 2014
Các câu lệnh phân cách bởi dấu ``;`` chạy tuần tự 
$ date; sleep 2; date
Wed Oct 15 00:21:31 ICT 2014
Wed Oct 15 00:21:33 ICT 2014
Bài viết thực hiện trên:
$ bash --version | grep -o 'version \d.* '
version 3.2.51(1)-release
Hết phần 1.
Tham khảo:
man 1 bash