Back to basic: Linux Command Line - Part 2
Bài đăng này đã không được cập nhật trong 5 năm
Nối tiếp bài trước hôm nay chúng ta sẽ tìm hiểu thêm một số kiến thức thường gặp khi làm việc với terminal.
Shell là gì?
Trước tiên, chúng ta nhắc đến khái niệm shell
.
Từ đầu đến giờ chúng ta mới chỉ nhắc đến cách chạy các lệnh mà chưa hiểu cách các lệnh chạy như thế nào.
Các bạn có thể nghe nhiều đến từ "nhân Linux", "nhân hệ điều hành", nhân hay kernel có nhiệm vụ là giao tiếp với hardware. Và nhân thì thường đi kèm với vỏ, ví dụ đập vỏ quả óc chó để ăn nhân bên trong Vỏ ở đây tức là shell, shell có nhiệm vụ là cầu nối giao tiếp giữa user và kernel. Do không cùng chung ngôn ngữ nên cần có 1 người thông dịch, shell chính là người làm việc đó, shell là 1 intepreter, thông dịch command từ input của user, chuyển đến kernel, và lấy ouput trả về hiển thị cho người dùng.
Có nhiều loại shell trong Linux nhưng thông dụng nhất có thể kể đến đó là bash
, zsh
, fish
, sh
...
Các lệnh sinh ra ở đâu?
Trong Linux có thể kể đến các loại lệnh sau:
- Những executable program hay binary. Những lệnh này thường là những chương trình được viết bằng C or C++ hay Go... và được biên dịch thành file executable (file nhị phân - mã máy nên có thể thực thi trực tiếp, các bạn có thể quen thuộc với các file có đuôi
.exe
trên Windows, nhưng trên Linux thường thì không có đuôi cụ thể nào), hoặc là những script được viết bằng Bash, Ruby, Python, PHP... - Loại thứ hai gọi là shell builtin. Builtin commands là những lệnh do shell cung cấp. Ví dụ lệnh thường dùng
pwd
,cd
là những shell builtin, giống như những cú pháp, function mà 1 ngôn ngữ lập trình cung cấp. - Loại thứ 3 đó là shell function. Nó giống như những function thông thường trong lập trình, nhưng những function này là của shell do đó nó có thể thực thi được.
- Loại cuối cùng là alias, loại này được tạo thành từ những lệnh có sẵn khác bằng cách nhóm lại hoặc với 1 số tham số cố định. Chẳng hạn, khi dùng git chúng ta hay dùng lệnh
git status
, chúng ta có thể rút gọn lại bằng alias:gst="git status"
, sau đấy thay vì gõgit status
chúng ta có thể gõ lệnhgst
thay thế
Để kiểm tra, debug xem lệnh thuộc kiểu nào có thể dùng các lệnh sau:
type
,which
VD:type ls
,which ls
,type git
"Thêm" lệnh vào terminal như thế nào??
Từ các loại lệnh kể trên, thì chúng ta có thể có 3 cách để tạo ra lệnh:
- Tạo các executable program => Vấn đề là tạo ra rồi để file này ở đâu?
- Viết shell function => Viết ở đâu?
- Tạo alias => Viết ở đâu?
Biến môi trường PATH
Khi làm với một số web framework như Rails, Laravel chúng ta hay gặp khái niệm biến môi trường hay env. Từ cái tên ta có thể đoán được ý nghĩa của nó: biến => có thể thay đổi, môi trường => phụ thuộc vào môi trường, tức là các giá trị phụ thuộc vào môi trường mà program, app chạy, nó ảnh hưởng đến hoạt động của program.
Ở đây có 1 biến môi trường mà có thể bạn biết lâu rồi đó PATH
, giá trị của biến này chính là đường dẫn đến các thư mục chưa executable program. Ví dụ, khi bạn gõ lệnh ls
, shell sẽ tìm trong các thư mục được chỉ định trong PATH
xem có file nào tên là ls
hay không, nếu có shell sẽ thực thi file đó. Những thư mục được khai báo trước sẽ được ưu tiên hơn.
Khai báo biến môi trường
Giống như khi bạn làm việc với một số web framework, bạn sẽ khai báo biến môi trường vào file .env
, sau đó khi ứng dụng được khởi chạy nó sẽ load file .env
này và gán giá trị cho các biến môi trường.
Shell cũng hoạt động với cơ chế tương tự, mỗi loại shell sẽ đọc biến môi trường từ 1 file riêng biệt.
Đối với bash
thì file đó là .bashrc
ở thư mục home từ là ~/.bashrc
, với zsh
là file ~/.zshrc
.
Lưu ý: chạy lệnh
echo $0
đế biết terminal của bạn đang sử dụng shell nào
Để thêm hoặc sửa biến môi trường chúng ta thường thêm vào cuối những file này.
Ví dụ thêm 1 biến môi trường:
export MY_ENV=secret
# or
export MY_ENV="secret value"
Lưu ý tên biến, dấu
=
và giá trị phải nằm liền nhau và không có dấu cách ở giữa. Nếu value có dấu cách thì phải dùng'
hoặc"
để quote lạiMY_VAR = value # Wrong MY_VAR = value has spaces # Wrong MY_VAR='value has spaces' MY_VAR="value of $OTHER_VAR"
Biến môi trường PATH mặc định của hệ thống thường sẽ có giá trị như thế này:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Lưu ý: các thư mục được cách nhau bằng dấu
:
khác với Windows dùng dấu;
=> Các thư mục mặc định chứa executable file đó là/usr/local/sbin
,/usr/local/bin
,/usr/sbin
,/usr/bin
,/sbin
,/bin
theo thứ tự ưu tiên giảm giần. Chẳng hạn cả 2 thư mục/usr/local/bin
và/usr/bin
đều có file tên làmy_command
, nhưng do/usr/local/bin
được khai báo trước nên shell sẽ sử dụng command/usr/local/bin/my_command
.
Để thêm 1 thư mục vào PATH thì ta thêm vào file ~/.bashrc
hoặc ~/.zshrc
, chẳng hạn thêm thư mục bin của composer:
export PATH=/home/ubuntu/.composer/vendor/bin:$PATH
Lưu ý: khi khai báo biết thì ta viết
MY_ENV=value
nhưng khi sử dụng biến thì ta phải tham chiếu qua tên$MY_ENV
. Ví dụ chạy lệnhecho $PATH
để kiểm tra những thư mục được đăng ký trong PATH.Sau khi thay đổi file này thì bạn cần restart terminal hoặc dùng lệnh
source ~/.bashrc
để load lại config của shell.
Biến môi trường tạm thời
Ta cũng có thể khai báo biến môi trường tạm thời, tức là biến đó chỉ áp dụng khi chạy 1 câu lệnh:
MY_ENV_VAR=temporary_var php -r 'echo getenv("MY_ENV_VAR");' # Output: temporary_var
echo $MY_ENV_VAR # No value
Thử đoán xem kết quả của lệnh này là như thế nào nhé
PATH='' ls
Hoặc ta cũng có thể khai báo biến môi trường mà giá trị của nó có phạm vi trong 1 phiên làm việc (session) bằng cách:
MY_SESSION_VAR=session_value
# OR
export MY_SESSION_VAR=session_value
Bạn có thể tham khảo thêm sự khác nhau giữa có export
và không có export
ở đây => https://stackoverflow.com/a/1158231/6595322
Qua các khái niệm trên ta có thể suy ra các cách "thêm lệnh" cho terminal như sau.
Thêm thư mục vào PATH
Cách này dùng cho trường hợp thư mục đó chứa nhiều file executable.
/home/ubuntu/.composer/vendor/bin
├── phpcbf -> ../squizlabs/php_codesniffer/bin/phpcbf
└── phpcs -> ../squizlabs/php_codesniffer/bin/phpcs
Tạo symbolic link
Hiểu nôm na là tạo shortcut đến file executable vào 1 trong các thư mục trong PATH
.
Chẳng hạn ta có file composer.phar được tải về vào thư mục ~/Download/
, thay vì mỗi lần chạy cần phải ~/Download/composer.phar
thì chúng ta có thể tạo symbolic link vào thư mục /usr/local/bin
:
sudo ln -s ~/Downloads/composer.phar /usr/local/bin/composer
Và sau đó khi cần dùng composer ta chỉ cần gõ composer
Tạo alias
Giống như việc khai báo biến môi trường, alias
cũng được khai báo trong các file ~/.bashrc
, ~/.zshrc
:
alias composer="~/Downloads/composer.phar"
Tạo shell function
Lại giống như việc khai báo alias
, bạn cũng có thể khai shell function
bên trong ~/.bashrc
, ~/.zshrc
:
function composer-install() {
if [ ! -f composer.json ]; then
composer init
fi
composer install
}
Cách này thường chỉ để custom những command có logic phức tạp, và cần nhiều kiến thức liên quan đến lập trình shell. Trong ví dụ trên là thực hiện khởi tạo composer nếu thư mục chưa có file composer.json và sau đó là thực hiện composer install.
Tạo các file script từ PHP, Python, Sh
Thực chất file composer.phar
nó cũng là 1 dạng file script được viết bằng PHP, bạn có thể mở file này vào có thể thấy nội dung phần đầu file như sau:
Dòng đầu tiên là 1 comment, nhưng nó cũng khá đặc biệt:
#!/usr/bin/env php
Shell xem dòng đặc biệt này (bắt đầu bằng #!
) như một chỉ dẫn để thực thi nội dung file. Ví dụ, đoạn code ở trên chỉ dẫn cho shell nội dung của file này sẽ được thực thi bằng lệnh /usr/bin/env php
, tức là khi ta chạy lệnh ./composer.phar
thì shell sẽ thông dịch ra là /usr/bin/env php ./composer.phar
Tương tự PHP, chúng ta có thể viết các script bằng các ngôn ngữ lập trình khác như Bash hay Python rồi sau đó thực hiện thêm thư mục vào PATH
, tạo symbolic link hay tạo alias để tạo lệnh global cho terminal.
#!/usr/bin/env bash
echo 'Hello from Bash'
#!/usr/bin/env python
print('Hello from Python')
File ownership, permissions, sudo là cái chi?
Trong Linux các file đều có tính sở hữu (ownership), tức là mỗi file đều thuộc về 1 owner user và thuộc về 1 group và những user không sở hữu file này gọi là others. Các đối tượng này lại có quyền hạn (permissions) nhất định đối với file. Các quyền cơ bản bao gồm read - r, write - w và execute - x (thực thi).
- r: user có quyền đọc file, xem nội dung file
- w: user có quyền ghi file, sửa file
- x: user có quyền thực thi file tức là "chạy lệnh"
Đến đây, ta thấy 1 điều kiện quan trọng để thực thi executable file đó là user (đang login vào hệ thống) phải có quyền x đối với file đó.
Trong ví dụ trên, lệnh ls -l
cho ta thông tin về owner và permission của file /usr/local/bin/composer
:
- Owner: user
ubuntu
có cả 3 quyềnrwx
- Group: các user thuộc nhóm
ubuntu
có cả 3 quyềnrwx
- Others: các user khác chỉ có 2 quyền
r-x
, ký từ-
xuất hiện ở vị trí thứ 2 tương ứng với không có quyền ghi w
Hoặc có thể xem bằng giao diện như trên.
Ngoài cách biểu diễn permission qua rwx
ta có thể thể hiện thông qua số:
- 4: tương đương với quyền read r
- 2: tương đương với quyền write w
- 1: tương đương với quyền execute x
=>
rwx
<=> 4 + 2 + 1 = 7,r-x
<=> 4 + 1 = 5,rw-
<=> 4 + 2 = 6
Bạn có nhớ các hướng dẫn thường bảo bạn chạy lệnh chmod 777 file_abc
không? Vậy 777 ở đây tức là cả owner user, các user trong group và các user others đều có quyền 7 tương ứng với rwx.
Hoặc 755 tương đương với owner => rwx, group => r-x, others => r-x
Thay đổi permission
Dùng lệnh chmod
để thay đổi quyền user, group và other đối với file, folder, sử dụng dấu +
để thêm quyền, và dấu -
để xóa quyền. Ví dụ:
chmod u+x my_script
=> Thêm quyền thực thi x
cho user owner
chmod u-x my_script
=> Xóa quyền thực thi x
của user owner
chmod ug+rwx my_script
=> Thêm quyền rwx
cho user owner và group
Tương tự ta có g
đại diện cho group, o
đại diện cho other, và a
đại diện cho tất cả user. Bạn đoán được kết quả của những lệnh sau chứ.
chmod a+x my_script
chmod a+rx my_script
chmod uo+rw-x my_script
Một cách dùng khác của chmod
đó là sử dụng số:
chmod 777 my_script
chmod 755 my_script
chmod 664 my_script
Thay đổi owner và group
Ta cũng có thể thay đổi owner và group của 1 file.
Thay đổi owner:
sudo chown patty myfile
=> Thay đổi owner của myfile
sang cho user patty
.
Thay đổi group:
sudo chgrp whales myfile
=> Thay đổi group của myfile
sang group whales
Hoặc ta cũng có thể thay đổi owner và group cùng 1 lúc:
sudo chown patty:whales myfile
Mỗi user và group đều được gắn với 1 ID, chúng ta có thể sử dụng ID để thay cho user name, group name.
sudo chown 1000:1000 myfile
Để xem ID của user ta dùng lệnh
id <tên_user>
hoặcid
là mặc định cho user đang login hoặc thông qua các biến môi trường$UID
,$GID
,$USER
sudo chown $UID:$GID otherfile
SUDO
sudo
có thể tạm dịch là superuser do.
Chắc hẳn bạn đã gặp các trường hợp lỗi "Permission denied", nguyên nhân là do bạn thao tác trên các file mà bạn không có quyền. Và cách giải quyết duy nhất đó là yêu cầu quyền trên các file đó.
Nếu bạn prefix các command với tiền tố sudo
thì command đó sẽ được thực thi dưới quyền cao cấp. Các quyền này là bắt buộc nếu bạn muốn thực hiện một số task liên quan đến hệ thống, chẳng hạn bạn muốn cài LAMP stack, bạn phải restart server sau khi thay đổi file config, chạy docker, thay đổi owner, permission của file thuộc user khác,... hoặc thậm chí là tác vụ shudown hoặc reboot máy.
Khi chạy lệnh với sudo
lần đầu nó sẽ hỏi password của user đang thực hiện, nhớ là password của user hiện tại chứ không phải password của user root. Password sẽ được ghi nhớ trong 15 phút giúp bạn không phải nhập lại password nhiều lần.
Nên nhớ là chỉ khi thực hiện thao tác với những file mà user hiện tại không có quyền, nếu đang ở thư mục home của user thì tất tiên mặc định là không cần sudo
.
Vấn đề là đã phân quyền rồi lại có thêm sudo
để làm gì, nếu user nào cũng có thể chạy sudo thì phân quyền để làm gì nữa? Tất nhiên là không phải user nào cũng có quyền sử dụng sudo
. Những user có quyền sử dụng sudo
được khai báo trong file /etc/sudoers
, và mặc định là các user thuộc group sudo có thể thực thi lệnh sudo
:
TIPS: khi nhập password trên terminal thì sẽ không có hiện tượng gì xảy ra trên terminal (chẳng hạn khi nhập password trên web form thì nó sẽ hiển thị các dấu chấm đen thay thế), có thể gây khó khăn khi sử dụng. Tuy nhiên điều này có thể thay đổi để cho terminal hiển thị dấu * khi nhập password bằng cách sửa config trong file
/etc/sudoers
thông qua lệnhvisudo
:sudo visudo
Và thay đổi:
Defaults env_reset
Thành:
Defaults env_reset,pwfeedback
Summary
Nội dung của phần này xin được tạm dừng tại đây hy vọng các bạn có thêm 1 chút kiến thức để tự tin làm việc với command line hơn.
Theo dự định thì mình sẽ viết về một số vấn đề sau:
- Cài đặt phần mềm
- SSH
- Chuyển hướng nhập xuất stdin, stdout
- Nén và giải nén file
- Checksum file là gì và để làm gì?
- Lập trình shell script
- Text processing
- Process management
- Service management (SysV, Upstart, systemd)
- ...
Nhưng thôi mình để cho ai có hứng thú thì tự tìm hiểu thêm
Dưới đây là slide mình làm để tổng kết một số kiến thức cơ bản về command line. Rất mong sự góp ý của các bạn.
Reference:
All rights reserved