Các công cụ của shell và viết ngôn ngữ kịch bản
Ở trong buổi học này, chúng tôi sẽ trình bày vài điều cơ bản trong việc sử dụng bash như một ngôn ngữ kịch bản và giới thiệu một số công cụ của shell được sử dụng thường xuyên trên môi trường dòng lệnh trong các công việc hằng ngày của bạn.
Shell Scripting - Ngôn ngữ kịch bản Shell
Chúng ta đã làm quen với việc thực hiện các lệnh bằng shell (vỏ) và pipe (liên kết) chúng lại với nhau thành một quy trình. Tuy nhiên, trong một vài trường hợp, bạn sẽ cần phải thực thi hàng loạt câu lệnh và sử dụng các cấu trúc điều khiển như câu điền kiện hoặc vòng lặp.
Ngôn ngữ shell là bước tiếp theo để có thể sử dụng những thứ phức tạp hơn. Hầu hết các shell đều có một ngôn ngữ kịch bản riêng với những cú pháp riêng biệt để tương tác với biến, cấu trúc điều khiển.Điều đặc biệt khiến ngôn ngữ shell khác biệt khi so sánh chúng với các ngôn ngữ kịch bản khác chính là ngôn ngữ shell đã được tối ưu cho việc thực thi các tác vụ liên quan tới shell (ở môi trường dòng lệnh). Do đó, việc tạo quy trình cho lệnh (pipe), lưu kết quả vào file, đọc dữ liệu từ thiết bị nhập chuẩn là những thứ nguyên thuỷ trong khi viết shell, điều này khiến shell script dễ dàng để sử dụng hơn là những ngôn ngũ kịch bản tổng quát. Ở trong phần này chúng ta sẽ sử dụng bash để lập trình shell vì bash rất phổ biến.
Ghi chú (người dịch):
- pipeline: Đối với ngành khoa học máy tính, một pipeline là một sự liên kết các tác vụ được sắp xếp sao cho đầu ra của một tác vụ trong quy trình sẽ là đầu vào của tác vụ tiếp theo. Các bạn có thể hiểu pipeline là quy trình. pipe là cách kết nối các tác vụ để tạo nên một pipeline. Trong ngữ cảnh của shell, các tác vụ này có thể hiểu đơn giản là các command (lệnh).
Để gán giá trị cho biến bằng bash, sử dụng cú pháp foo=bar
và truy cập giá trị của biến bằng cú pháp $foo
.
Lưu ý foo = bar
sẽ không chạy bởi vì câu lệnh sẽ được biên dịch thành việc gọi chương trình foo
với đối số là =
và bar
.
Tóm lại, dấu khoảng cách đóng vai trò dấu phân cách các đối số trong các ngôn ngữ shell. Việc này có thể sẽ hơi khó hiểu và gây nhầm lẫn ở giai đoạn đầu, nên hãy luôn kiểm tra việc này.
Chuỗi có thể được khai báo bằng dấu '
và "
trong bash, nhưng chúng không bằng nhau.
Chuỗi được khai báo bằng '
là chuỗi theo nghĩa đen (giá trị cụ thể) và sẽ không được thay thế bằng các giá trị của biến, trong khi chuỗi được khai báo bằng "
thì có.
foo=bar
echo "$foo"
# prints bar
echo '$foo'
# prints $foo
Như hầu hết các ngôn ngữ lập trình khác, bash cũng hỗ trợ các cấu trúc điều khiển như if
, case
, while
và for
.
Và tất nhiên, bash
hỗ trợ các hàm nhận vào các đối số và thực hiện các tác vụ. Đây là một ví dụ về hàm có chức năng là tạo một thư mục và cd
vào nó.
mcd () {
mkdir -p "$1"
cd "$1"
}
Ở đây $1
mang ý nghĩa là đối số được truyền vào đầu tiên cho hàm.
Không như các ngôn ngữ khác, bash
sử dụng một tập hợp các biến đặc biệt để chỉ đến các đối số, mã lỗi và các biến liên quan. Ở dưới là một danh sách của chúng. Các bạn có thể tìm một danh sách đầy đủ và chi tiết hơn ở đây.
$0
- Tên của shell hoặc tên của shell script$1
tới$9
- Các đối số của lệnh$1
là đầu tiên và tiếp tục.$@
- Tất cả đối số$#
- Số lượng đối số$?
- Mã kết quả của lệnh trước (thành công hay thất bại)$$
- PID (Process identification number ) của lệnh!!
- Chỉ đến lệnh phía trước, tính cả tham số. Một cách sử dụng cơ bản của nó là thực hiện lệnh nếu lệnh trước đó thất bại bởi vì thiếu quyền truy cập(permission denied). Bạn có thể tái thực hiện lệnh với quyền sudo bằng cáchsudo !!
.$_
- Đối số cuối cùng của lệnh phía trước. nếu bạn đang sử dụng shell bằng chế độ tương tác trực tiếp, bạn có thể lấy giá trị này bằng việc gõEsc
và.
.
Các lệnh thường sẽ trả về kết quả ở STDOUT
, lỗi ở STDERR
, và một mã kết quả phục vụ cho mục đích lập trình.
Mã kết quả hoặc mã kết thúc (return code or exit status) là cách để các đoạn mã/lệnh tương tác với nhau nhằm xác định việc thực thi như thế nào.
Giá trị của mã kết thúc là 0 biểu hiện mọi thứ vẫn bình thường, khác 0 nghĩa là có lỗi xảy ra.
Mã kết thúc có thể được sử dụng để xử lý điều kiện thực hiện các lệnh tiếp theo bằng việc sử dụng toán tử &&
và ||
(toán tử and
và or
), cả 2 điều là toán tử short-circuiting. Các lệnh có thể được ngăn cách với nhau trên cùng một dòng sử dụng dấu ;
(semicolon-chấm phẩy). Lệnh true
sẽ luôn trả về mã kết thúc là 0 và lệnh false
sẽ luôn trả về mã kết thúc là 1.
Ghi chú (người dịch):
- PID: mỗi chương trình khi chạy trên shell hoặc hệ điều hành thì sẽ được chạy dưới dạng mã nhĩ phân. Chương trình là trạng thái của mã nguồn đã được build ra file thực thi. Khi chương trình chạy thì chương trình sẽ được nạp vào bộ nhớ để CPU có thể thực thi, lúc này thì chương trình gọi là tiến trình (process). Mỗi tiến trình đang chạy sẽ có một
id
là số để phân biệt các tiến trình với nhau. - short-circuiting: Có thể hiểu là toán tử mạnh số ở tầng thấp, thao tác trực tiếp với từng bóng bán dẫn (chỉ 1 một ví dụ - không có nghĩa là viết code như trên thì ta thực sự đang thao tác tới tầng thấp),
- STDIN: Thiết bị nhập chuẩn, thường là bàn phím
- STDOUT: Thiết bị xuất chuẩn, là màn hình console để gõ lệnh
Xem một vài ví dụ sau:
false || echo "Oops, fail"
# Oops, fail
true || echo "Will not be printed"
#
true && echo "Things went well"
# Things went well
false && echo "Will not be printed"
#
true ; echo "This will always run"
# This will always run
false ; echo "This will always run"
# This will always run
Một ứng dụng khác là lưu trữ giá trị đầu ra của một lệnh vào một biến. Việc này có thể thực hiện bằng việc sử dụng command substitution.
Bất cứ khi nào bạn sử dụng cú pháp $( CMD )
, shell sẽ thực thi lệnh CMD
và sau đó lấy kết quả được trả về và thay thế tại chỗ.
Ví dụ, đối với lệnh sau for file in $(ls)
, shell thưc thi lện $(ls)
trước và sau đó mới thực hiện lặp qua từng giá trị.
Một ứng dụng tương đương nhưng ít phổ biến hơn là process substitution, <( CMD )
sẽ thực thi lệnh CMD
và lưu trữ giá trị của kết quả vào một file tạm thời và thay thế bằng tên file tạm thời đó. Điều này rất hữu dụng khi những lệnh đọc dữ liệu từ file thay vì thiết bị nhập chuẩn STDIN.
Ví dụ diff <(ls foo) <(ls bar)
sẽ chỉ ra những files khác nhau trong 2 thư mục foo
và bar
.
Bỏi vì có quá nhiều thông tin, hãy xem các ví dụ cụ thể kỹ hơn. Đoạn mã sẽ lặp qua các đối số được cung cấp, thực thi lệnh grep
để so sánh đối số với chuỗi foobar
, và thêm chuỗi foobar
này vào file như là 1 dòng comment nếu trong file không có từ bất kỳ chuỗi foobar
.
#!/bin/bash
echo "Starting program at $(date)" # Date will be substituted
echo "Running program $0 with $# arguments with pid $$"
for file in "$@"; do
grep foobar "$file" > /dev/null 2> /dev/null
# When pattern is not found, grep has exit status 1
# We redirect STDOUT and STDERR to a null register since we do not care about them
if [[ $? -ne 0 ]]; then
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done
Câu lệnh điều kiện kiểm tra giá trị của biến $?
khác 0.
Bash đã cài đặt nhiều lệnh so sánh - tham khảo ở danh sách đầy đủ ở trang web manpage dành cho test
.
Khi thực hiện lệnh so sánh, cố gắng sử dụng 2 cặp dấu ngoặc vuông [[ ]]
thay vì chỉ 1 [ ]
.Điều này sẽ giảm tỉ lệ lỗi xuống mặc dù điều này không tương thích với sh
. Giải thích chi tiết có thể tìm ở đây.
Khi mã nguồn được thực thi, việc truyền nhiều đối số tương tự nhau khá phổ biến. Bash cung cấp cách để làm cho mọi chuyện đơn giản hơn, khả năng expanding expressions (mở rộng biểu thức) bằng việc gom nhóm các filename expansion (định dạng mở rộng của file). Kỹ thuật này được gọi là shell globbing.
- Wildcards - Khi nào cần sử dụng tìm kiếm dựa trên wildcard, bạn có thể dùng
?
và*
để tìm kiểm 1 hoặc bất kỳ số lượng các ký tự, theo thứ tự. Ví dụ, có các file saufoo
,foo1
,foo2
,foo10
vàbar
, lệnhrm foo?
sẽ xoáfoo1
andfoo2
trong khi đórm foo*
sẽ xoá tất cả ngoại trừ filebar
. - Cặp dấu ngoặc nhọn
{}
- Khi có các chuỗi tương tự nhau làm tham số cho nhiều lệnh, bạn có thể sử dụng cặp dấu ngoặc nhọn để mở rộng tự động. Điều này rất phổ biến khi di chuyển, chuyển đổi các files.
Chú thích (người dịch):
- wildcard : những ký tự đặc biệt sử dụng để biểu một hoặc nhiều ký tự khác
convert image.{png,jpg}
# Will expand to
convert image.png image.jpg
cp /path/to/project/{foo,bar,baz}.sh /newpath
# Will expand to
cp /path/to/project/foo.sh /path/to/project/bar.sh /path/to/project/baz.sh /newpath
# Globbing techniques can also be combined
mv *{.py,.sh} folder
# Will move all *.py and *.sh files
mkdir foo bar
# This creates files foo/a, foo/b, ... foo/h, bar/a, bar/b, ... bar/h
touch {foo,bar}/{a..h}
touch foo/x bar/y
# Show differences between files in foo and bar
diff <(ls foo) <(ls bar)
# Outputs
# < x
# ---
# > y
Viết mã bash
đôi khi khó khăn và không trực quan. Có một vài công cụ như shellcheck giúp chúng ta tìm lỗi ở trong mã sh/bash của bạn.
Lưu ý rằng mã không nhất thiết phải được viết ở bằng bash thì mới có thể chạy được ở giao diện dòng lệnh. Đây là một đoạn mã Python sẽ trả về kết quả là các tham số được truyền vào với thứ tự nghịch đảo.
#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
print(arg)
Kernel biết thực thi một đoạn mã sử dụng trình biên dịch của python thay vì shell, bởi vì chúng ta đã đính kèm dấu shebang ở đầu file script.
Đây là một ví dụ tốt để viết shebang
bằng lệnh env
để có thể giải quyết trường hợp nơi lưu trữ cách lệnh trong hệ thống, tằng cường khả năng di động của mã. Để xử lý vị trí, env
sẽ sử dụng biến môi trường PATH
đã được giới thiệu ở bài đầu tiên.
Ví dụ, dòng shebang
sẽ trông như thế này #!/usr/bin/env python
.
Một vài điều khác biệt giữa function (hàm) và script (mã) của shell cần ghi nhớ:
- Hàm phải được viết bằng ngôn ngữ của shell, còn script thì có thể viết bằng bất cứ ngôn ngữ nào. Đây là lý do lại sao shebang lại quan trọng trong các script.
- Hàm sẽ được tải khi định nghĩa của nó được đọc. Script thì được tải mỗi khi nó được thực thi. Điều này khiến cho việc tải function nhanh hơn một chút, tuy nhiên nếu có sự thay đổi trong hàm thì cần phải tải lại định nghĩa hàm.
- Hàm được thực thi trong tại môi trường shell đang chạy, còn script thì được được chạy ở một tiến trình (process) riêng. Cho nên, functions có thể thay đổi các biến môi trường của shell, ví dụ như là thư mục hiện hành, trong khi script thì không thể. Các script có thể truy cập tới các biến môi đã được export bằng việc sử dụng
export
. - Giống như các ngôn ngữ lập trình khác, hàm là phương pháp mạnh mẽ để đạt được khả năng mô-đun, tái sử dụng, minh bạch của mã shell. Thông thường thì các script sẽ có bao gồm các định nghĩa hàm
Công cụ của shell
Cách sử dụng lệnh
Tại thời điểm này, bạn có thể đang tự hỏi rằng làm thế nào để tìm các tuỳ chọn (flags) dành cho các lệnh giống như ls -l
, mv -i
and mkdir -p
.
Tổng quát hơn, đối với một lệnh, làm sao ta có thể tìm ra các chức năng của chúng và cái tuỳ chọn tương ứng?
Chúng ta có thể bắt đầu tìm google cho câu hỏi này, tuy nhiên UNIX thì có trước cả StackOverflow, và có 1 cách truyền thống để lấy các thông tin này.
Như chúng ta đã biết ở trong bài đầu tiên, cách tiếp cận đầu tiên là gọi lệnh với với tuỳ chọn -h
hoặc --help
. Cách tiếp cận chi tiết hơn là sử dụng lệnh man
.
Viết ngắn gọn của từ manual
, man
cung cấp 1 trang hướng dẫn sử dụng (gọi là manpage) cho một lệnh bất kỳ.
Ví dụ man rm
sẽ trả về tất cả các tác vụ của lệnh rm
cùng với các tuỳ chọn mà lệnh cung cấp, cùng với tuỳ chọn -i
đã được giới thiệu trước đó.
Thực tế, những đường link tôi đã đính kèm cho các lệnh thực chất một phiên bản trực tuyến của Linux manpage. Thậm chí những lệnh không có sẵn mà bạn cần phải cài đặt cũng có một trang manpage nếu lập trình viên có viết chúng và đóng gói chúng cùng quá trình cài đặt.
Đối với những công cụ tương tác trực tiếp ví dụ như những thứ dựa trên ncurses
, hướng dẫn sử dụng của các lệnh thường được truy cập ngay trong chương trình bằng việc viết :help
hoặc ?
.
Đôi khi các trang manpages có thể cung cấp rất nhiều thông tin chi tiết mô tả về lệnh, khiến nó trở lên khó để xác định xem những tuỳ chọn/ cú pháp nào để sử dụng trong các trường hợp cơ bản.TLDR pages là một giải pháp đơn giản, tập trung vào việc đưa ra các ví dụ cụ thể và trường hợp sử dụng của từng lệnh giúp bạn nhanh chóng nhận ra các tuỳ chọn nào cần thiết.
Cụ thể hơn, tôi thường hay sử dụng trang tldr
cho các lệnh tar
và ffmpeg
hơn là manpages.
Finding files
Một trong các công việc thường xuyên lặp đi lặp lại đối với lập trình viên là tìm files hoặc thư mục.
Các hệ thống dựa trên UNIX thường có sẵn lệnh find
, một công cụ tuyệt vời của shell để tìm kiếm file. find
sẽ tìm kiếm đệ quy các file có tên khớp với một vài tiêu chuẩn nào đó.
# Find all directories named src
find . -name src -type d
# Find all python files that have a folder named test in their path
find . -path '*/test/*.py' -type f
# Find all files modified in the last day
find . -mtime -1
# Find all zip files with size in range 500k to 10M
find . -size +500k -size -10M -name '*.tar.gz'
Ngoại trừ các thứ được kể trên, find
có thể thực hiện nhiều tác vụ trên nhiều file là kết quả của câu truy vấn.
Đặc điểm này thực sử hữu dụng để đơn giản hoá những công việc nhàm chán
# Delete all files with .tmp extension
find . -name '*.tmp' -exec rm {} \;
# Find all PNG files and convert them to JPG
find . -name '*.png' -exec convert {} {}.jpg \;
Mặc dù sự phổ biến của find
, cú pháp của nó thực sự khó ghi nhớ.
Ví dụ, để thực thiện công việc đợn giản như là tìm kiếm các file khớp với mẫu PATTERN
, bạn phải viết câu lệnh như thế này find -name '*PATTERN*'
(hoặc -iname
nếu muốn không phân biệt chữ hoa, chữ thường).
Bạn có thể bắt đầu viết một vài bí danh (alias) cho những trường hợp trên, nhưng tư tưởng của shell khuyến khích tìm kiếm những thứ thay thế tốt hơn.
Luôn nhớ rằng, một trong những thuộc tính thú vị nhất của shell là bạn chỉ đang gọi những chương trình mà thôi, cho nên bạn có thể tìm (hoặc thậm chí là viết mới) những thứ thay thế cho cùng 1 công việc.
Điển hình, fd
là một chương trình đơn giản, nhanh và dễ sử dụng thay thế cho find
.
Chương trình này hỗ trợ một vài thứ mặc định xịn xò như tô màu kết quả, sử dụng biểu thức chính quy (regex - regular expression), và hỗ trợ unicode. Và tất nhiên, theo quan điểm cá nhân của tôi (tác giả), một cú pháp dễ hơn.
Một trường hợp cụ thể, cú pháp để tìm một mẫu như PATTERN
là fd PATTERN
Hầu hết các ý kiến đều đồng ý rằng find
và fd
rất tốt, nhưng chắc một vài người sẽ tự hỏi rằng về độ hiệu quả của việc tìm kiếm so với việc đánh chỉ mục hoặc sử dụng cơ sở dữ liệu cho việc tìm kiếm nhanh.
Và đây là điều mà locate
phát triển.
locate
sử dụng cơ sở dữ liệu được cập nhật sử dụng updatedb
.
Trong hầu hết hệ thông, updatedb
được cập nhật hàng ngày thông qua cron
.
Bởi vì lý do này nên có một sử đánh đổi giữa tốc độ và độ trực tuyến (fresness).
Một điều nữa là find
và các công cụ tương tự thì có thể tìm kiếm file dựa trên nhiều thuộc tính khác như kích cỡ, ngày chỉnh sửa, quyền hạn, trong khi locate
chỉ sử dụng tên file.
So sánh chi tiết có thể tìm ở đây.
Finding code
Tìm kiếm file bằng tên khá hữu ích, tuy nhiên thao tác tìm kiếm dựa trên nội dung file cũng cần thiết không kém.
Một trường hợp phổ biến là tìm tất cả các file chứa nhiều hơn một mẫu, cùng với nơi mà kết quả xuất hiện (số dòng).
Để làm việc này, hầu hết hệ thống lõi UNIX đều cung cấp grep
, một công cụ giúp tìm kiếm các mẫu dựa trên văn bản đầu vào.
grep
là một công cụ cực kỳ mạnh mà chúng ta sẽ tìm hiểu thêm, chi tiết về nó ở bài sau (data wrangling).
Ở hiện tại, grep
cung cấp rất nhiều tuỳ chọn (flags) khiến nó trở nên linh hoạt.
Một vài tuỳ chọn tôi hay dùng là -C
để lấy ngữ cảnh kết quả và -v
để nghịch đảo kết quá, ví dụ, tìm những đoạn không khớp với mẫu. Ví dụ grep -C 5
sẽ in ra 5 dòng trước và sau kết quả. Khi cần thực hiện việc tìm kiếm nhiều files, bạn có thể sử dụng -R
bởi vì tuỳ chọn này sẽ đi vào từng file ở trong thư mục và tìm kiếm kết quả.
Nhưng grep -R
có thể được cải tiến bằng nhiều cách, ví dụ như bỏ qua .git
folded, sử dụng CPU đa nhân để hỗ trợ, et cetera
Có nhiều công cụ thay thế grep
dã được phát triển, bao gồm ack, ag và rg.
Tất cả những công cụ trên rất tuyệt vời và cung cấp cùng chức năng.
Hiện tại tôi đang sử dụng ripgrep rg
, bởi vì nó rất nhanh và dễ.
Ví dụ:
# Find all python files where I used the requests library
rg -t py 'import requests'
# Find all files (including hidden files) without a shebang line
rg -u --files-without-match "^#!"
# Find all matches of foo and print the following 5 lines
rg foo -A 5
# Print statistics of matches (# of matched lines and files )
rg --stats PATTERN
Lưu ý rằng với find
/fd
, điều quan trọng là các vấn đề này có thể được giải quyết rất là dễ dàng, còn việc sử dụng công cụ nào thì không quan trọng.
Finding shell commands
Chúng ta tìm hiểu việc tìm kiếm các files và code, khi bắt đầu sử dụng shell nhiều hơn, bạn có thể muốn tìm các lệnh mà đã gõ ở vài thời điểm. Điều đầu tiên cần phải biết đó là mũi tên lên sẽ trả về bạn lệnh cuối cùng, và nếu bạn giữ nó thì nó sẽ từ từ duyệt qua lịch sử shell của bạn.
Lệnh history
sẽ giúp bạn truy cập tới lịch sử của shell .
Nó sẽ in ra thiết bị xuất chuẩn lịch sử của shell.
Nếu bạn muốn tìm kiếm lịch sử, ta có thể nối đầu ra với grep
để thực hiện việc tìm kiếm.
history | grep find
sẽ in ra các lệnh có chứa chuỗi “find”.
Trong hầu hết các shell, bạn có thể sử dụng Ctrl+R
để thực hiện việc tìm kiếm lịch sử.
Sau khi nhấn phím Ctrl+R
, bạn có thể nhập một chuổi mà bạn muốn tìm kiếm ở trong lịch sử.
Và nếu bạn tiếp tục giữ phím, bạn sẽ đi vào vòng lặp các kết quả.
Điều này cũng có làm được bằng việc nhấn phím UP/DOWN (mũi tên lên/xuống) đối với zsh
Một điều thú vị là Ctrl+R
là sử dụng fzf.
fzf
là một công cụ tìm kiếm tổng quát có thể được sử dụng với rất nhiều lệnh khác.
Do đó, nó được sử dụng để tìm kiếm nhanh chóng dựa trên lịch sử của bạn và hiển thị kết quả một cách tiện lợi và dễ nhìn.
Một thứ hay ho về lịch sử shell nữa mà tôi rất thích đó là gợi ý tự động dựa trên lịch sử (history-based autosuggestion). Lần đầu được giới thiệu bởi fish, tính năng này sẽ giúp bạn hoàn thành lệnh shell hiện tại dựa trên lịch sử các lệnh gần nhất có các điểm chung. Tính năng này có thể được kích hoạt ở zsh và nó sẽ giúp cuộc đời bạn dễ dàng hơn khi làm việc với shell.
Bạn cũng có thể tuỳ chỉnh các hành vi của lịch sử, ví dụ chặn các lệnh có các dấu khoảng trắng dư thừa. Điều này sẽ có ích khi nhập mật khẩu hoặc các thông tin nhạy cảm khác. Để kích hoạt chức năng này, thêm HISTCONTROL=ignorespace
vào file .bashrc
hoặc setopt HIST_IGNORE_SPACE
vào file .zshrc
.
Nếu có sai sót không thêm khoảng cách thừa, bạn có thể xoá thủ công các chúng ở trong .bash_history
hoặc .zhistory
Directory Navigation
Ở đoạn trước, chúng ta đã mặc định rằng chúng ta đang ở đúng nơi cần thực hiện các tác vụ đó. Tuy nhiên làm thể để nào để di chuyển nhanh chóng giữa các thư mục. Có rất nhiều cách đơn giản cho bạn thử, ví dụ như viết một alias hoặc tạo symlink sử dụng ln -s, nhưng thực tế rằng là các lập trình viên tìm ra cách thông minh và đầy chất xám hơn tại thời điểm hiện tại.
Với phương châm của khoá học này, bạn sẽ đối mặt với những tình huống phổ biến nhất.
Tìm những file và/hoặc thư mục thường xuyên được sử dụng có thể được được xử lý bằng fasd
vàautojump
.
Fasd xếp hạng các files và thư mục dựa trên frecency, nghĩa là , cả 2 tần số và gần đây.
Mặc định, fasd
thêm một lệnh z
mà bạn có thể dủng để có thể nhanh chóng cd
chỉ với một chuỗi của thự mục frecent. Ví dụ Nếu bạn thường xuyên di chuyển tới /home/user/files/cool_project
thì bạn chỉ đơn giản là sử dụng z cool
để di chuyển tới đó. Sử dụng autojump
thì cú pháp tương tự sẽ là j cool
.
Một vài công cụ giúp bạn nhanh chóng nắm bắt được cấu trúc thư mục : tree
, broot
thậm chí là một trình quản lý file toàn diện như nnn
hoặc ranger
.
Exercises
- Đọc
man ls
và viết các lệnhls
giúp liệt kê các thư mục theo yêu cầu sau- Liệt kê tất cả các file, kể cả file bị ẩn
- Kích thước được liệt kê phải ở dạng dễ dọc (vd: 454M thay vì 454279954)
- File được sắp xếp dựa trên thời gian truy cập cuối cùng
- Tô màu các kết quả
Ouput ví dụ
-rw-r--r-- 1 user group 1.1M Jan 14 09:53 baz drwxr-xr-x 5 user group 160 Jan 14 09:53 . -rw-r--r-- 1 user group 514 Jan 14 06:42 bar -rw-r--r-- 1 user group 106M Jan 13 12:12 foo drwx------+ 47 user group 1.5K Jan 12 18:08 ..
-
Viết các hàm bash
marco
vàpolo
có chức năng sau Bất cứ khi nàomarco
được gọi, thư mục hiện hành được lưu trữ (bất kể lưu trữ kiểu gì), và khipolo
được thực thi, bất kể thư mục hiện hành là gì, phải luôncd
đến thư mục hiện hành gần nhất được lưu trữ bởimacro
Để tiện cho việc tìm lỗi, bạn có thể viết code vào 1 filemarco.sh
và tải (lại) định nghĩa hàm vào shell bằng việc thực thisource marco.sh
-
Giả sử bạn có một lệnh hiếm như xảy ra lỗi, Và để tìm lỗi cho nó, bạn cần ghi nhớ lại kết quả của nó nhưng sẽ tốn thời gian để nó có thể bị lỗi. Hãy viết một đoạn mã giúp bạn chạy hàng loạt các đoạn mã đó tới khi nó xảy ra lỗi và lưu trữ lại kết quả đó (cả STDOUT và STDERR) vào file và in ra mọi thứ vào cuối cùng. Điểm cộng nếu như bạn có thể tìm ra được việc chạy bao nhiêu lần thì gặp lỗi
Chương trình cần tìm lỗi
#!/usr/bin/env bash n=$(( RANDOM % 100 )) if [[ n -eq 42 ]]; then echo "Something went wrong" >&2 echo "The error was using magic numbers" exit 1 fi echo "Everything went according to plan"
-
Như chúng ta đã đề cập ở trên thì tuỳ chọn
-exec
củafind
thực sự rất mạnh mẽ trong việc thực thi các tác vụ trên file chúng ta cần tìm kiếm. Tuy nhiên, nếu chúng ta muốn làm điều gì đó với toàn bộ file, ví dụ như là nén lại thành một file zip? Dựa vào các kiến thức trên, Lệnh sẽ nhận đầu vào từ các đối số và cả STDIN. Khi piping các lệnh, chúng ta thực chất đang kết nối STDOUT với STDIN, nhưng một vài lệnh nhưtar
lại nhận đầu vào từ những đối số (nhận tên file); Để tạo cầu nối cho sự bất tiện này,xargs
sẽ giúp chúng ta thực thi lệnh sử dụng STDIN như đối số. Ví dụ,ls | xargs rm
sẽ xoá tất cả các file ở thư mục hiện hành.Công việc của bạn là viết một lệnh tìm kiếm đệ quy tất cả các HTML files trong các folder và tạo một file zip. Lưu ý rằng lệnh bạn viết phải hoạt động cả với những file có dấu khoảng trắng trong tên (gợi ý: tìm hiểu vể
-d
củaxargs
)Nếu bạn sử dụng macOS, lưu ý rằng lệnh
find
mặc định của BSD thì khác với cái của GNU coreutils. Bạn có thể sử dụng-print0
đối vốifind
và-0
đối vớixargs
. Là một người sử dụng macOS thì bạn phải lưu ý rằng những tiện ích của môi trường giao diện dòng lệnh khác với của GNU, bạn cũng có thể cài đặt phiên bản của GNU trên macOS nếu muốn bằng việc sử dụng brew - (Nâng cao) Việt một lệnh hoặc một đoạn mã tìm kiếm các file được chỉnh sửa gần đây nhất. Hoặc tổng quát hơn, bạn có thể liệt kê các file dựa trên lịch sử chỉnh sửa của chúng?
Licensed under CC BY-NC-SA.