Tìm hiểu các shell và so sánh các sell

Ở 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

1 và truy cập giá trị của biến bằng cú pháp

mcd () {
    mkdir -p "$1"
    cd "$1"
}

2. Lưu ý

mcd () {
    mkdir -p "$1"
    cd "$1"
}

3 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

4 với đối số là

mcd () {
    mkdir -p "$1"
    cd "$1"
}

5 và

mcd () {
    mkdir -p "$1"
    cd "$1"
}

6. 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

7 và

mcd () {
    mkdir -p "$1"
    cd "$1"
}

8 trong bash, nhưng chúng không bằng nhau. Chuỗi được khai báo bằng

mcd () {
    mkdir -p "$1"
    cd "$1"
}

7 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

8 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ư

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

1,

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

2,

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

3 và

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

4. Và tất nhiên,

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

5 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à

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

6 vào nó.

mcd () {
    mkdir -p "$1"
    cd "$1"
}

Ở đây

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

7 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,

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

5 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.

  • 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

    9 - Tên của shell hoặc tên của shell script
  • 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

    7 tới

    #!/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

    1 - Các đối số của lệnh

    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

    7 là đầu tiên và tiếp tục.
  • #!/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

    3 - Tất cả đối số
  • #!/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

    4 - Số lượng đối số
  • #!/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

    5 - Mã kết quả của lệnh trước (thành công hay thất bại)
  • #!/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

    6 - PID (Process identification number ) của lệnh
  • #!/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

    7 - 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ách

    #!/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

    8.
  • #!/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

    9 - Đố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õ

    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

    0 và

    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

    1.

Các lệnh thường sẽ trả về kết quả ở

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

2, lỗi ở

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

3, 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ử

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

4 và

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

5 (toán tử

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

6 và

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

7 ), 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

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

8 (semicolon-chấm phẩy). Lệnh

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

9 sẽ luôn trả về mã kết thúc là 0 và lệnh

#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
    print(arg)

0 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 #!/usr/local/bin/python import sys for arg in reversed(sys.argv[1:]):
    print(arg)  
    
    1 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

#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
    print(arg)

2, shell sẽ thực thi lệnh

#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
    print(arg)

3 và sau đó lấy kết quả được trả về và thay thế tại chỗ. Ví dụ, đối với lệnh sau

#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
    print(arg)

4, shell thưc thi lện

#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
    print(arg)

5 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,

#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
    print(arg)

6 sẽ thực thi lệnh

#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
    print(arg)

3 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ụ

#!/usr/local/bin/python
import sys
for arg in reversed(sys.argv[1:]):
    print(arg)

8 sẽ chỉ ra những files khác nhau trong 2 thư mục

mcd () {
    mkdir -p "$1"
    cd "$1"
}

4 và

mcd () {
    mkdir -p "$1"
    cd "$1"
}

6.

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

# 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'

1 để so sánh đối số với chuỗi

# 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'

2, và thêm chuỗi

# 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'

2 này vào file như là 1 dòng comment nếu trong file không có từ bất kỳ chuỗi

# 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'

2.

#!/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

#!/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

5 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

# 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'

6. 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

# 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'

7 thay vì chỉ 1

# 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'

8.Đ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

# 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'

9. 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

    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 \;

    0 và

    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 \;

    1 để 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 sau

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    4,

    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 \;

    3,

    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 \;

    4,

    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 \;

    5 và

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    6, lệnh

    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 \;

    7 sẽ xoá

    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 \;

    3 and

    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 \;

    4 trong khi đó

    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

    0 sẽ xoá tất cả ngoại trừ file

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    6.
  • Cặp dấu ngoặc nhọn

    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

    2 - 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ã

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

5 đô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

# 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

4 bằng lệnh

# 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

5 để 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í,

# 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

5 sẽ sử dụng biến môi trường

# 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

7 đã được giới thiệu ở bài đầu tiên. Ví dụ, dòng

# 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

4 sẽ trông như thế này

# 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

9.

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 -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 .. 0.
  • 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ư

 -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 ..

1,

 -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 ..

2 and

 -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 ..

3. 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

 -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 ..

4 hoặc

 -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 ..

5. Cách tiếp cận chi tiết hơn là sử dụng lệnh

 -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 ..

6. Viết ngắn gọn của từ

 -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 ..

7,

 -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 ..

6 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ụ

 -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 ..

9 sẽ trả về tất cả các tác vụ của lệnh

mcd () {
    mkdir -p "$1"
    cd "$1"
}

00 cùng với các tuỳ chọn mà lệnh cung cấp, cùng với tuỳ chọn

mcd () {
    mkdir -p "$1"
    cd "$1"
}

01 đã đượ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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

02, 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

03 hoặc

# 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 \;

0.

Đô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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

05 cho các lệnh

mcd () {
    mkdir -p "$1"
    cd "$1"
}

06 và

mcd () {
    mkdir -p "$1"
    cd "$1"
}

07 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08, một công cụ tuyệt vời của shell để tìm kiếm file.

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08 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,

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08, 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

12, bạn phải viết câu lệnh như thế này

mcd () {
    mkdir -p "$1"
    cd "$1"
}

13 (hoặc

mcd () {
    mkdir -p "$1"
    cd "$1"
}

14 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,

mcd () {
    mkdir -p "$1"
    cd "$1"
}

15 là một chương trình đơn giản, nhanh và dễ sử dụng thay thế cho

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08. 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ư

mcd () {
    mkdir -p "$1"
    cd "$1"
}

12 là

mcd () {
    mkdir -p "$1"
    cd "$1"
}

18

Hầu hết các ý kiến đều đồng ý rằng

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08 và

mcd () {
    mkdir -p "$1"
    cd "$1"
}

15 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à

mcd () {
    mkdir -p "$1"
    cd "$1"
}

21 phát triển.

mcd () {
    mkdir -p "$1"
    cd "$1"
}

21 sử dụng cơ sở dữ liệu được cập nhật sử dụng

mcd () {
    mkdir -p "$1"
    cd "$1"
}

23. Trong hầu hết hệ thông,

mcd () {
    mkdir -p "$1"
    cd "$1"
}

23 được cập nhật hàng ngày thông qua

mcd () {
    mkdir -p "$1"
    cd "$1"
}

25. 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à

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

21 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

# 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'

1, 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.

# 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'

1 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,

# 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'

1 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à

mcd () {
    mkdir -p "$1"
    cd "$1"
}

31 để lấy ngữ cảnh kết quả và

mcd () {
    mkdir -p "$1"
    cd "$1"
}

32 để nghịch đảo kết quá, ví dụ, tìm những đoạn không khớp với mẫu. Ví dụ

mcd () {
    mkdir -p "$1"
    cd "$1"
}

33 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

34 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

35 có thể được cải tiến bằng nhiều cách, ví dụ như bỏ qua

mcd () {
    mkdir -p "$1"
    cd "$1"
}

36 folded, sử dụng CPU đa nhân để hỗ trợ, et cetera Có nhiều công cụ thay thế

# 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'

1 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

38, 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08/

mcd () {
    mkdir -p "$1"
    cd "$1"
}

15, đ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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

41 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

# 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'

1 để thực hiện việc tìm kiếm.

mcd () {
    mkdir -p "$1"
    cd "$1"
}

43 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

44 để thực hiện việc tìm kiếm lịch sử. Sau khi nhấn phím

mcd () {
    mkdir -p "$1"
    cd "$1"
}

44, 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à

mcd () {
    mkdir -p "$1"
    cd "$1"
}

44 là sử dụng .

mcd () {
    mkdir -p "$1"
    cd "$1"
}

47 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

48 vào file

mcd () {
    mkdir -p "$1"
    cd "$1"
}

49 hoặc

mcd () {
    mkdir -p "$1"
    cd "$1"
}

50 vào file

mcd () {
    mkdir -p "$1"
    cd "$1"
}

51. 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

52 hoặc

mcd () {
    mkdir -p "$1"
    cd "$1"
}

53

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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

54 và

mcd () {
    mkdir -p "$1"
    cd "$1"
}

55. 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,

mcd () {
    mkdir -p "$1"
    cd "$1"
}

54 thêm một lệnh

mcd () {
    mkdir -p "$1"
    cd "$1"
}

57 mà bạn có thể dủng để có thể nhanh chóng

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

6 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

mcd () {
    mkdir -p "$1"
    cd "$1"
}

59 thì bạn chỉ đơn giản là sử dụng

mcd () {
    mkdir -p "$1"
    cd "$1"
}

60 để di chuyển tới đó. Sử dụng

mcd () {
    mkdir -p "$1"
    cd "$1"
}

55 thì cú pháp tương tự sẽ là

mcd () {
    mkdir -p "$1"
    cd "$1"
}

62.

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 :

mcd () {
    mkdir -p "$1"
    cd "$1"
}

63,

mcd () {
    mkdir -p "$1"
    cd "$1"
}

64 thậm chí là một trình quản lý file toàn diện như

mcd () {
    mkdir -p "$1"
    cd "$1"
}

65 hoặc

mcd () {
    mkdir -p "$1"
    cd "$1"
}

66.

Exercises

  1. Đọc

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    67 và viết các lệnh

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    68 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 ..
  2. Viết các hàm bash

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    69 và

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    70 có chức năng sau Bất cứ khi nào

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    69 được gọi, thư mục hiện hành được lưu trữ (bất kể lưu trữ kiểu gì), và khi

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    70 được thực thi, bất kể thư mục hiện hành là gì, phải luôn

    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

    6 đến thư mục hiện hành gần nhất được lưu trữ bởi

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    74 Để tiện cho việc tìm lỗi, bạn có thể viết code vào 1 file

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    75 và tải (lại) định nghĩa hàm vào shell bằng việc thực thi

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    76
  3. 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

    mcd () {

    mkdir -p "$1"  
    cd "$1"  
    
    }

    0

Như chúng ta đã đề cập ở trên thì tuỳ chọn

mcd () {
    mkdir -p "$1"
    cd "$1"
}

77 của

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08 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ư

mcd () {
    mkdir -p "$1"
    cd "$1"
}

06 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,

mcd () {
    mkdir -p "$1"
    cd "$1"
}

80 sẽ giúp chúng ta thực thi lệnh sử dụng STDIN như đối số. Ví dụ,

mcd () {
    mkdir -p "$1"
    cd "$1"
}

81 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ể

mcd () {
    mkdir -p "$1"
    cd "$1"
}

82 của

mcd () {
    mkdir -p "$1"
    cd "$1"
}

80)

Nếu bạn sử dụng macOS, lưu ý rằng lệnh

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08 mặc định của BSD thì khác với cái của GNU coreutils. Bạn có thể sử dụng

mcd () {
    mkdir -p "$1"
    cd "$1"
}

85 đối vối

mcd () {
    mkdir -p "$1"
    cd "$1"
}

08 và

mcd () {
    mkdir -p "$1"
    cd "$1"
}

87 đối với

mcd () {
    mkdir -p "$1"
    cd "$1"
}

80. 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