Chuyển hướng cả stdout và stderr sang pipe. Bash Shell: Giới thiệu

Điều gì là cần thiết?

Bạn nên làm quen với dòng lệnh cũng như các khái niệm lập trình cơ bản. Mặc dù đây không phải là một hướng dẫn lập trình nhưng nó giải thích (hoặc ít nhất là cố gắng giải thích) nhiều khái niệm cơ bản.
Sử dụng tài liệu này

Tài liệu này có thể cần thiết trong các trường hợp sau:

Bạn có ý tưởng liên quan đến lập trình và có nhu cầu thực hiện quá trình code một số shell script.

Ý tưởng lập trình của bạn chưa đủ cụ thể và cần có hướng dẫn bổ sung.

Bạn có muốn xem một số tập lệnh shell và nhận xét làm ví dụ cho việc tạo tập lệnh của riêng mình không?

Bạn đang di chuyển từ DOS/Windows (hoặc đã làm như vậy) và muốn tạo các tệp "hàng loạt".

Bạn hoàn toàn là một người mọt sách và đọc mọi cách thực hiện có trong tay.

Các kịch bản đơn giản nhất

HƯỚNG DẪN này cố gắng cung cấp cho bạn một số hướng dẫn về lập trình shell, chỉ dựa trên các ví dụ.

Trong phần này, bạn sẽ tìm thấy những đoạn script nhỏ có thể sẽ hữu ích cho bạn khi thành thạo một số kỹ thuật.

Kịch bản "xin chào thế giới" truyền thống

#!/bin/bash echo Xin chào thế giới!

Tập lệnh này chỉ chứa hai dòng. Đầu tiên cho hệ thống biết chương trình nào đang được sử dụng để chạy tệp.

Dòng thứ hai là hành động duy nhất mà tập lệnh này thực hiện, in "Xin chào thế giới" tới thiết bị đầu cuối.

Nếu bạn nhận được một cái gì đó như ./hello.sh: Command không tìm thấy. , thì có lẽ dòng đầu tiên "#!/bin/bash" sai; chạy bash ở đâu hoặc nhìn vào việc tìm bash để tìm ra dòng này sẽ là gì.

Kịch bản sao lưu đơn giản

#!/bin/bash
tar -cZf /var/my-backup.tgz /home/me/

Trong tập lệnh này, thay vì in thông báo trên thiết bị đầu cuối, chúng tôi tạo một kho lưu trữ tar của thư mục chính của người dùng. Kịch bản KHÔNG nhằm mục đích sử dụng thực tế. Một kịch bản hiệu quả hơn sẽ được trình bày sau trong tài liệu này. Dự trữ bản sao.

Tất cả về lý thuyết chuyển hướng và xem nhanh

Có 3 bộ mô tả tệp: stdin - đầu vào tiêu chuẩn, stdout - đầu ra tiêu chuẩn và stderr - lỗi tiêu chuẩn.

Các tính năng chính của bạn:
chuyển hướng thiết bị xuất chuẩn sang tập tin
chuyển hướng stderr sang tập tin
chuyển hướng stdout sang stderr
chuyển hướng stderr sang stdout
chuyển hướng stderr và stdout sang tập tin
chuyển hướng stderr và stdout sang stdout
chuyển hướng stderr và stdout sang stderr

1 có nghĩa là thiết bị xuất chuẩn và 2 có nghĩa là thiết bị xuất chuẩn. Một lưu ý nhanh để hiểu rõ hơn: với lệnh less, bạn có thể xem cả thiết bị xuất chuẩn, vẫn còn trong bộ đệm và thiết bị xuất chuẩn, được in ra màn hình. Tuy nhiên, nó sẽ bị xóa khi bạn cố gắng "duyệt" bộ đệm.

Ví dụ: thiết bị xuất chuẩn ra tập tin

Hành động này ghi đầu ra tiêu chuẩn của chương trình vào một tệp.

ls -l > ls-l.txt

Điều này tạo ra một tệp có tên "ls-l.txt". Nó sẽ chứa mọi thứ mà bạn sẽ thấy nếu bạn chỉ chạy lệnh "ls -l". 3.3 Ví dụ: stderr vào tập tin
Hành động này ghi luồng lỗi tiêu chuẩn của chương trình vào một tệp.
grep da * 2> grep-errors.txt

Điều này tạo ra một tệp có tên "grep-errors.txt". Nó sẽ chứa phần lỗi tiêu chuẩn của đầu ra lệnh "grep;da;*". 3.4 Ví dụ: stdout thành stderr

Hành động này ghi đầu ra tiêu chuẩn của chương trình vào cùng một tệp với luồng lỗi tiêu chuẩn.
grep da * 1>&2

Ở đây đầu ra tiêu chuẩn của lệnh được gửi đến lỗi tiêu chuẩn. Bạn có thể thấy điều này theo những cách khác nhau. 3.5 Mẫu: stderr 2 stdout

Hành động này ghi luồng lỗi tiêu chuẩn của chương trình vào cùng vị trí với đầu ra tiêu chuẩn.
grep * 2>&1
Ở đây, luồng lỗi tiêu chuẩn của lệnh được gửi đến đầu ra tiêu chuẩn; Nếu bạn đặt kết quả (|) thành ít hơn, bạn sẽ thấy các dòng thường bị mất (do được ghi vào lỗi tiêu chuẩn) sẽ được lưu trong trường hợp này (vì chúng ở trên đầu ra tiêu chuẩn). 3.6 Ví dụ: stderr và stdout vào file

Hành động này đặt tất cả đầu ra của chương trình vào một tệp. Đây là một lựa chọn tốt cho các công việc định kỳ: nếu bạn muốn lệnh chạy hoàn toàn im lặng.
rm -f $(find / -name core) &> /dev/null

Điều này (giả sử là cron) sẽ xóa bất kỳ tệp nào được gọi là "lõi" trong bất kỳ thư mục nào. Hãy nhớ rằng bạn phải hoàn toàn chắc chắn về chức năng của lệnh nếu bạn muốn ghi đè kết quả đầu ra của nó. 4. Băng tải

Phần này giải thích một cách khá đơn giản và thực tế về cách sử dụng băng tải và lý do bạn có thể cần chúng.
Nó là gì và tại sao bạn nên sử dụng nó?

Đường ống cung cấp cho bạn khả năng sử dụng (tác giả tin rằng điều này khá đơn giản) đầu ra của một chương trình làm đầu vào của chương trình khác.

Ví dụ: một đường dẫn đơn giản với sed

Đây là một cách rất đơn giản để sử dụng băng tải.

ls -l | sed -e "s//u/g"

Điều xảy ra ở đây là lệnh ls;-l ban đầu được thực thi và đầu ra của nó, thay vì hiển thị trên màn hình, được gửi đến chương trình sed, chương trình này sẽ in những gì cần có trên màn hình. 4.3 Ví dụ: thay thế cho ls;-l;*.txt

Điều này có thể phức tạp hơn đáng kể so với ls;-l;*.txt, nhưng nó được trình bày ở đây chỉ để minh họa cách làm việc với các đường dẫn chứ không phải để quyết định giữa hai phương pháp liệt kê.

ls -l | grep "\.txt$"

Ở đây, đầu ra của ls -l được gửi tới grep, từ đó grep sẽ in các dòng khớp với biểu thức chính quy "\.txt$". 5. Biến

Bạn có thể sử dụng các biến theo cách tương tự như trong bất kỳ ngôn ngữ lập trình nào. Không có kiểu dữ liệu. Biến trong bash có thể là số, ký tự hoặc chuỗi ký tự.

Bạn không nên khai báo một biến. Trong thực tế, việc gán một giá trị cho con trỏ của nó đã tạo ra nó rồi.

Ví dụ: "Xin chào thế giới!" sử dụng biến

#!/bin/bash
STR="Xin chào thế giới!"
tiếng vang $STR

Dòng thứ hai tạo một biến có tên STR và gán cho nó giá trị chuỗi "Xin chào thế giới!". Sau đó, VALUE của biến này được trích xuất bằng cách thêm dấu "$" ở đầu. Hãy nhớ (cố gắng lên) rằng nếu bạn không sử dụng dấu "$", đầu ra của chương trình có thể khác. Có lẽ không phải là thứ bạn cần.
Ví dụ: tập lệnh sao lưu rất đơn giản (hiệu quả hơn)
#!/bin/bash
OF=/var/my-backup-$(date +%Y%m%d).tgz #OF - Tệp đầu ra - tệp đầu ra
tar -cZf $OF /home/me/

Kịch bản này giới thiệu một khái niệm khác. Trước hết, bạn nên giải quyết dòng thứ hai. Hãy chú ý đến biểu thức "$(date +%Y%m%d)". Nếu bạn chạy tập lệnh này, bạn sẽ nhận thấy rằng nó thực thi lệnh bên trong dấu ngoặc đơn, chặn đầu ra của nó.

Lưu ý rằng trong tập lệnh này, tên tệp đầu ra sẽ thay đổi hàng ngày dựa trên định dạng của phím lệnh date;(+%Y%m%d). Bạn có thể thay thế điều này bằng một bài tập định dạng khác.
Những ví dụ khác:
tiếng vang ls
tiếng vang $(ls)

Biến cục bộ

Các biến cục bộ có thể được tạo bằng cách sử dụng từ khóa local.
#!/bin/bash
XIN CHÀO=Xin chào
xin chào(
địa phương HELLO=Thế giới
echo $Xin chào
}
echo $Xin chào
Xin chào
echo $Xin chào
Ví dụ này đủ để chỉ ra cách sử dụng các biến cục bộ.

Câu điều kiện

Câu lệnh có điều kiện cho bạn khả năng quyết định có thực hiện một hành động hay không; quyết định được đưa ra khi tính giá trị của biểu thức.

Chỉ là lý thuyết

tồn tại một số lượng lớn các dạng câu lệnh điều kiện. Dạng cơ bản là một câu lệnh if then, trong đó "câu lệnh" chỉ được thực thi nếu "biểu thức" đánh giá là "true". "2<1" - это выражение, имеющее значение "ложь", в то время как "2>1" - "đúng".

Có các dạng câu lệnh điều kiện khác như: biểu thức if thì câu lệnh 1 câu lệnh khác câu lệnh 2. Ở đây "toán tử1" được thực thi nếu "biểu thức" là đúng; nếu không, "câu lệnh 2" sẽ được thực thi.

Một dạng khác của câu lệnh điều kiện là: nếu biểu thức1 thì toán tử1 khác nếu biểu thức2 thì toán tử2 khác toán tử3. Biểu mẫu này chỉ thêm chuỗi "ELSE IF "biểu thức2" THEN "câu lệnh2"", khiến "câu lệnh2" được thực thi nếu "biểu thức2" được đánh giá là "đúng". Mọi thứ khác đều tương ứng với ý tưởng của bạn về điều này (xem các biểu mẫu trước đó).

Một vài lời về cú pháp:
Cấu trúc cơ bản của câu lệnh "if" trong bash trông như thế này:
nếu [biểu thức];
sau đó
mã nếu "biểu thức" là đúng.
fi

Ví dụ: ví dụ cơ bản về câu lệnh điều kiện if;..;then
#!/bin/bash
nếu [ "foo" = "foo"]; sau đó

fi

Nếu biểu thức bên trong dấu ngoặc vuông là đúng thì mã được thực thi sẽ nằm sau từ "then" và trước từ "fi", cho biết phần cuối của mã sẽ được thực thi khi đáp ứng điều kiện.
Ví dụ: ví dụ cơ bản câu lệnh có điều kiện if;..;then;...;else
#!/bin/bash
nếu [ "foo" = "foo"]; sau đó
biểu thức echo đánh giá là đúng
khác

fi

Ví dụ: Câu lệnh điều kiện có biến
#!/bin/bash
T1="foo"
T2="thanh"
nếu [ "$T1" = "$T2" ]; sau đó
biểu thức echo đánh giá là đúng
khác
biểu thức echo đánh giá là sai
fi
vòng lặp for, while và cho đến khi
Trong phần này, bạn sẽ làm quen với các vòng lặp for, while và cho đến khi.
Vòng lặp for hơi khác so với vòng lặp trong các ngôn ngữ lập trình khác. Trước hết, nó mang lại cho bạn cơ hội để thực hiện hành động nhất quán phía trên các "từ" trong dòng.
Vòng lặp while thực thi một đoạn mã nếu biểu thức đang được kiểm tra là đúng; và dừng nếu nó sai (hoặc gặp phải ngắt vòng lặp được chỉ định rõ ràng trong mã thực thi).
Vòng lặp cho đến gần giống với vòng lặp while. Sự khác biệt duy nhất là mã được thực thi nếu biểu thức đang được kiểm tra là sai.
Nếu bạn cho rằng while và cho đến khi rất giống nhau thì bạn đã đúng.

Ví dụ cho vòng lặp

#!/bin/bash
cho tôi ở $(ls); LÀM
mục tiếng vang: $i
xong

Trong dòng thứ hai, chúng ta biểu thị i dưới dạng một biến nhận các giá trị khác nhau có trong $(;ls;).

Nếu cần, dòng thứ ba có thể dài hơn; hoặc có thể có vài dòng trước khi hoàn thành (dòng thứ 4).

"done" (dòng thứ 4) cho biết mã sử dụng $i sắp kết thúc và $i được cấp một giá trị mới.

Kịch bản này không nhằm mục đích có tầm quan trọng lớn. Cách sử dụng vòng lặp for hữu ích hơn là sử dụng nó để chỉ chọn một số tệp nhất định trong ví dụ trước.
C-như cho

fiesh đề xuất thêm dạng vòng lặp này. Đây là vòng lặp for, giống nhất với vòng lặp for trong C, Perl, v.v.

#!/bin/bash
for i in `seq 1 10`;
LÀM
tiếng vang $i
xong
Ví dụ trong khi lặp lại:
#!/bin/bash
BỘ ĐẾM=0
while [ $COUNTER -lt 10 ]; LÀM
echo Bộ đếm là $COUNTER
hãy để COUNTER=COUNTER+1
xong

Tập lệnh này “mô phỏng” cấu trúc “for” nổi tiếng (trong C, Pascal, Perl, v.v.).

Ví dụ cho đến vòng lặp:

#!/bin/bash
BỘ ĐẾM=20
cho đến [$COUNTER -lt 10]; LÀM
echo ĐẾM $COUNTER
hãy để ĐẾM-=1
xong

Chức năng

Cũng giống như bất kỳ ngôn ngữ lập trình nào khác, bạn có thể sử dụng các hàm để nhóm các đoạn mã lại với nhau theo cách hợp lý hơn, cũng như thực hành nghệ thuật đệ quy kỳ diệu.

Khai báo hàm chỉ là một mục nhập hàm my_func ( my_code ).

Việc gọi một hàm được thực hiện tương tự như cách gọi các chương trình khác. Bạn chỉ cần viết tên cô ấy.

Hàm ví dụ
#!/bin/bash
chức năng thoát (
lối ra
}
xin chào(
echo Xin chào!
}
Xin chào
từ bỏ
echo foo
Dòng 2-4 chứa chức năng "thoát". Dòng 5-7 chứa chức năng "xin chào". Nếu bạn không hiểu quy trình được thực hiện bởi tập lệnh này, hãy thử nó!

Cần lưu ý rằng không cần thiết phải khai báo các hàm theo thứ tự cụ thể nào.

Nếu bạn chạy tập lệnh, hãy lưu ý rằng hàm "xin chào" được gọi trước tiên và sau đó là hàm "thoát". Còn chương trình thì không bao giờ tới dòng thứ 10.

Ví dụ về hàm có tham số
#!/bin/bash
chức năng thoát (
lối ra
}
hàm e (
tiếng vang $1
}
Xin chào
thế giới mạng
từ bỏ
echo foo
Kịch bản này gần giống với kịch bản trước. Sự khác biệt chính là chức năng "e". Nó in ngay đối số đầu tiên mà nó nhận được. Các đối số trong hàm được xử lý giống như các đối số được truyền vào tập lệnh.

Giao diện người dùng

Sử dụng select để tạo menu đơn giản
#!/bin/bash
OPTIONS="Xin chào Thoát"
chọn tham gia $OPTIONS; LÀM
if [ "$opt" = "Thoát" ]; sau đó
echo xong
lối ra
elif [ "$opt" = "Xin chào" ]; sau đó
echo Xin chào thế giới
khác
thông thoáng
tiếng vang tùy chọn xấu
fi
xong
Nếu bạn chạy tập lệnh này, bạn sẽ thấy rằng đó là giấc mơ của các lập trình viên về một menu dựa trên văn bản. Có thể bạn sẽ nhận thấy rằng điều này rất giống với cấu trúc "for", nhưng thay vì lặp qua từng "từ" trong $OPTIONS, chương trình sẽ thăm dò ý kiến ​​người dùng.
Cách sử dụng dòng lệnh
#!/bin/bash
nếu [ -z "$1" ]; sau đó
sử dụng echo: thư mục $0
lối ra
fi
SRCD=$1 #SRCD - Thư mục SouRCe - thư mục nguồn

tar -cZf $TGTD$OF $SRCD
Bạn nên rõ kịch bản này làm gì. Biểu thức ở phần đầu tiên điều hành có điều kiện kiểm tra xem chương trình có nhận được đối số ($1) hay không. Nếu không, nó sẽ dừng chương trình và hiển thị cho người dùng một thông báo lỗi nhỏ. Phần còn lại của kịch bản bây giờ rõ ràng là tự giải thích.

Điều khoản khác

Đọc đầu vào của người dùng với read

Trong một số trường hợp, có thể cần phải yêu cầu người dùng nhập nội dung nào đó. Có nhiều cách khác nhau để làm điều này. Một cách là như sau:

#!/bin/bash
echo Vui lòng nhập tên của bạn
đọc TÊN
echo "Xin chào $NAME!"

Ngoài ra, bạn có thể nhận được nhiều giá trị cùng một lúc bằng cách sử dụng read. Ví dụ sau đây giải thích điều này:
#!/bin/bash
echo "Xin vui lòng nhập họ và tên của bạn"
đọc FN LN #FN - Họ - tên; LN - Họ - họ
echo "Xin chào! $LN, $FN !"

Tính toán số học

Tại dòng lệnh (hoặc shell), hãy thử gõ như sau:
tiếng vang;1;+;1
Nếu bạn mong đợi nhìn thấy "2", bạn sẽ thất vọng. Bạn nên làm gì nếu cần BASH để thực hiện các phép tính trên các con số của mình? Giải pháp là thế này:
echo;$((1+1))
Nhờ đó, kết luận sẽ “hợp lý” hơn. Ký hiệu này được sử dụng để đánh giá các biểu thức số học. Bạn cũng có thể làm điều đó như thế này:
tiếng vang;$
Nếu bạn cần sử dụng phân số hoặc toán học phức tạp hơn, bạn có thể sử dụng bc để đánh giá các biểu thức số học.
Khi tác giả chạy "echo;$" trong shell, nó trả về giá trị 0. Điều này là do nếu bash phản hồi, nó chỉ sử dụng các giá trị số nguyên. Nếu bạn chạy "echo;3/4|bc;-l", shell sẽ trả về giá trị đúng 0,75.

tìm kiếm bash

Từ tin nhắn từ Mike (xem phần "Lời cảm ơn"):

Bạn luôn sử dụng #!/bin/bash .. Bạn có thể cho một ví dụ về cách khám phá vị trí của bash không.
Tốt nhất nên sử dụng "locate bash", nhưng định vị không có sẵn trên tất cả các máy.
"find ./ -name bash" từ thư mục gốc thường hoạt động.
Bạn có thể kiểm tra các vị trí sau:
ls -l /bin/bash
ls -l /sbin/bash
ls -l /usr/local/bin/bash
ls -l /usr/bin/bash
ls -l /usr/sbin/bash
ls -l /usr/local/sbin/bash
(tác giả không thể nghĩ ngay đến bất kỳ thư mục nào khác... Anh ấy đã tìm thấy bash ở hầu hết những nơi này trên nhiều hệ thống khác nhau).

Bạn cũng có thể thử "bash nào".

Lấy giá trị trả về của chương trình

Trong bash, giá trị trả về của chương trình được lưu trữ trong một biến đặc biệt gọi là $?.

Ví dụ này minh họa cách chặn giá trị trả về của chương trình; tác giả cho rằng thư mục dada không tồn tại (điều này cũng được mike đề xuất).
#!/bin/bash
cd /dada &> /dev/null
echo rv: $?
cd $(pwd) &> /dev/null
echo rv: $?


Chặn đầu ra lệnh

Cái này kịch bản nhỏđại diện cho tất cả các bảng từ tất cả các cơ sở dữ liệu (giả sử bạn đã cài đặt MySQL). Ngoài ra, bạn nên nghĩ cách chuyển đổi lệnh "mysql" để sử dụng tên người dùng và mật khẩu phù hợp.
#!/bin/bash
DBS=`mysql -uroot -e"hiển thị cơ sở dữ liệu"`
cho b trong $DBS ;
LÀM
mysql -uroot -e"hiển thị bảng từ $b"
xong

Nhiều tập tin nguồn

Bạn có thể chạy nhiều tệp bằng lệnh nguồn.

LÀM__
11. Bàn
11.1 Toán tử so sánh chuỗi
(1) s1 = s2
(2) s1 != s2
(3) s1< s2
(4) s1 > s2
(5) -n s1
(6) -z s1
(1) s1 trùng với s2
(2) s1 không trùng với s2
(3) s1 đứng trước s2 theo thứ tự bảng chữ cái (theo ngôn ngữ hiện tại)
(4) s1 theo thứ tự bảng chữ cái sau s2 (theo ngôn ngữ hiện tại)
(5) s1 có giá trị khác 0 (chứa một ký tự trở lên)
(6) s1 có giá trị bằng 0
11.2 Ví dụ về so sánh chuỗi

So sánh hai chuỗi.
#!/bin/bash
S1="chuỗi"
S2="Chuỗi"
nếu [ $S1=$S2 ];
sau đó
echo "S1("$S1") không bằng S2("$S2")"
fi
nếu [ $S1=$S1 ];
sau đó
echo "S1("$S1") bằng S1("$S1")"
fi
Tại thời điểm này, tác giả cảm thấy cần phải trích dẫn một nhận xét từ email nhận được từ Andreas Beck về việc sử dụng if [ $1 = $2 ].
Đây không phải là một ý tưởng hay, vì nếu $S1 hoặc $S2 dòng trống Bạn sẽ nhận được một lỗi cú pháp. Sẽ dễ chấp nhận hơn nếu sử dụng x$1;=;x$2 hoặc "$1";=;"$2" .
11.3 Toán tử số học
+
-
*
% (phần còn lại)
11.4 Toán tử so sánh số học
-lt (<)
-gt (>)
-le (<=)
-ge (>=)
-eq (==)
-ne (!=)
Lập trình viên C chỉ cần chọn toán tử khớp với toán tử đã chọn trong ngoặc đơn.

Các lệnh hữu ích

Phần này được Kees viết lại (xem phần Lời cảm ơn).

Một số lệnh này thực tế có chứa đầy đủ ngôn ngữ lệnh. Ở đây chỉ giải thích những điều cơ bản về các lệnh như vậy. Để biết thêm chi tiết, hãy xem lại cẩn thận các trang man của từng lệnh.
sed (trình chỉnh sửa luồng)
Sed là một trình soạn thảo không tương tác. Thay vì thay đổi tệp bằng cách di chuyển con trỏ trên màn hình, bạn nên sử dụng tập lệnh hướng dẫn chỉnh sửa sed cùng với tên tệp bạn đang chỉnh sửa. Bạn cũng có thể coi sed như một bộ lọc. Hãy xem một số ví dụ:
$sed "s/to_be_replaced/replaced/g" /tmp/dummy
Sed thay thế chuỗi "to_be_replaced" bằng chuỗi "được thay thế" bằng cách đọc tệp /tmp/dummy. Kết quả được gửi đến đầu ra tiêu chuẩn (thường là bảng điều khiển), nhưng bạn cũng có thể thêm ">;capture" vào dòng trên để sed gửi đầu ra đến tệp "capture".
$sed 12, 18d /tmp/dummy
Sed hiển thị tất cả các dòng ngoại trừ dòng 12 đến 18. Tệp nguồn không được sửa đổi bởi lệnh này.
awk (thao tác tệp dữ liệu, truy xuất và xử lý văn bản)
Có một số lượng lớn cách triển khai ngôn ngữ lập trình AWK (các trình thông dịch phổ biến nhất là gawk từ dự án GNU và mawk "new awk".) Nguyên tắc khá đơn giản: AWK đang tìm kiếm một mẫu; Đối với mỗi mẫu phù hợp, một số hành động sẽ được thực hiện.
Tác giả đã tạo lại tệp giả chứa các dòng sau:
"kiểm tra123
Bài kiểm tra
tteesstt"

$awk "/test/ (in)" /tmp/dummy
kiểm tra123
Bài kiểm tra
Mẫu AWK tìm kiếm là "kiểm tra" và hành động AWK thực hiện khi gặp một dòng trong /tmp/dummy có chuỗi con "kiểm tra" là "in".
$awk "/test/ (i=i+1) END (print i)" /tmp/dummy
Nếu bạn đang tìm kiếm nhiều mẫu, hãy thay thế văn bản giữa các dấu ngoặc kép bằng "-f;file.awk". Trong trường hợp này, bạn có thể viết tất cả các mẫu và hành động vào tệp "file.awk".
grep (in các dòng khớp với mẫu tìm kiếm)
Chúng ta đã xem xét một số lệnh grep trong các chương trước để hiển thị các dòng khớp với một mẫu. Tuy nhiên, grep có thể làm được nhiều hơn thế.
$grep "tìm cái này" /var/log/messages -c
Chuỗi "tìm cái này" được tìm thấy 12 lần trong /var/log/messages.
wc (đếm dòng, từ và byte)
Trong ví dụ sau, bạn sẽ nhận thấy rằng kết quả đầu ra không như chúng ta mong đợi. Trong trường hợp này, tệp giả chứa văn bản tiếp theo:
" giới thiệu bash
cách kiểm tra tập tin"
$wc --words --lines --bytes /tmp/dummy
2 5 34 /tmp/giả
wc không quan tâm đến thứ tự của các tham số. Nó luôn xuất chúng theo thứ tự tiêu chuẩn:<число;строк><число;слов><число;байтов><имя;файла>.
sắp xếp (sắp xếp các dòng của tệp văn bản)

Trong trường hợp này, tệp giả chứa văn bản sau:
"b
c
Một"
$sort /tmp/giả
Đầu ra trông như thế này:
Một
b
c
Các lệnh không nên đơn giản như vậy :-)
bc (ngôn ngữ lập trình tính toán)
bc thực hiện các phép tính từ dòng lệnh (đầu vào từ một tệp, nhưng không thông qua chuyển hướng hoặc đường dẫn) cũng như từ giao diện người dùng. Ví dụ sau đây cho thấy một số lệnh. Lưu ý rằng tác giả đã sử dụng bc với tùy chọn -q để chặn thông báo nhắc nhở.
$bc -q
1 == 5
0
0.05 == 0.05
1
5 != 5
0
2 ^ 8
256
mét vuông(9)
3
trong khi (i != 9) (
tôi = tôi + 1;
in tôi
}
123456789
từ bỏ
tput (khởi tạo một thiết bị đầu cuối hoặc truy vấn cơ sở dữ liệu terminfo)
Một minh họa nhỏ về khả năng của tput:

$tput cốc 10 4
Dấu nhắc lệnh sẽ xuất hiện ở tọa độ (y10,x4).
đặt lại $tput
Màn hình xóa và lời nhắc xuất hiện tại (y1,x1). Lưu ý rằng (y0,x0) là góc trên cùng bên trái.
$tputcols
80 Hiển thị số ký tự có thể có theo hướng x.
Chúng tôi thực sự khuyên bạn nên làm quen với các chương trình này (tối thiểu). Có một số tiền rất lớn chương trình nhỏ, mang đến cho bạn cơ hội thực hiện một số phép thuật thực sự trên dòng lệnh.
[Một số ví dụ được lấy từ trang hướng dẫn sử dụng hoặc Câu hỏi thường gặp.]

Nhiều tập lệnh hơn

Áp dụng lệnh cho tất cả các file trong một thư mục.

Ví dụ: tập lệnh sao lưu rất đơn giản (hiệu quả hơn)

#!/bin/bash
SRCD="/home/" #SRCD - Thư mục SouRCe - thư mục nguồn
TGTD="/var/backups/" #TGTD - Thư mục TarGeT - thư mục cuối cùng
OF=home-$(date +%Y%m%d).tgz #OF - Tệp đầu ra - tệp đầu ra
tar -cZf $TGTD$OF $SRCD

Chương trình đổi tên tập tin

#!/bin/sh
# renna: đổi tên nhiều tệp bằng các quy tắc đặc biệt
# Tác giả - felix hudson Tháng 1 - 2000

#Trước hết, hãy xem các "chế độ" khác nhau mà chương trình này có.
#Nếu đối số đầu tiên ($1) phù hợp, chúng tôi thực hiện phần này
#chương trình và rời đi.

# Kiểm tra khả năng thêm tiền tố.
nếu [ $1 = p ]; sau đó

#Bây giờ chúng ta chuyển sang biến chế độ ($1) và tiền tố ($2)
tiền tố=$2 ; sự thay đổi ; sự thay đổi

# Phải kiểm tra xem có ít nhất một tệp được chỉ định hay không.
# Ngược lại, tốt hơn là không làm gì hơn là đổi tên không tồn tại
# các tập tin!!

nếu [$1 = ]; sau đó

lối ra 0
fi

# Vòng lặp for này xử lý tất cả các file mà chúng ta đã chỉ định
# chương trình.
# Nó thực hiện một lần đổi tên cho mỗi tệp.
cho tập tin trong $*
LÀM
mv $(file) $prefix$file
xong

#Sau đó chương trình sẽ được thoát.
lối ra 0
fi

# Kiểm tra điều kiện thêm hậu tố.
# Nếu không thì, phần này gần như giống với phần trước;
# vui lòng xem các bình luận có trong đó.
nếu [ $1 = s ]; sau đó
hậu tố=$2 ; sự thay đổi ; sự thay đổi
nếu [$1 = ]; sau đó
echo "không có tập tin nào được chỉ định"
lối ra 0
fi
cho tập tin trong $*
LÀM
mv $(file) $file$suffix
xong
lối ra 0
fi

# Kiểm tra điều kiện đổi tên bằng thay thế.
nếu [ $1 = r ]; sau đó
sự thay đổi
# Vì lý do bảo mật, tác giả đã đưa vào phần này để không làm hỏng bất kỳ tệp nào nếu người dùng
# không xác định phải làm gì:
nếu [$# -lt 3] ; sau đó
echo "Lỗi; thông tin nhập đúng: các tập tin renna r [biểu thức] [thay thế]..."
lối ra 0
fi

# Cùng xem những thông tin khác
CŨ=$1 ; MỚI=$2 ; sự thay đổi ; sự thay đổi

# Vòng lặp for này tuần tự đi qua tất cả các file mà chúng ta
# được gán cho chương trình.
# Nó thực hiện một lần đổi tên cho mỗi tệp bằng chương trình "sed".
# Cái này chương trình đơn giản từ dòng lệnh, phân tích tiêu chuẩn
# nhập và thay thế biểu thức chính quy bằng chuỗi đã cho.
# Ở đây chúng ta cung cấp cho sed một tên tệp (làm đầu vào tiêu chuẩn) và thay thế
# văn bản bắt buộc.
cho tập tin trong $*
LÀM
new=`echo $(file) | sed s/$(OLD)/$(NEW)/g`
mv $(tập tin) $mới
xong
lối ra 0
fi
# Nếu đến dòng này nghĩa là chương trình đã được cấp
# thông số không hợp lệ. Về vấn đề này, cần giải thích cho người dùng cách
# sử dụng
tiếng vang "sử dụng:"
echo "tập tin renna p [tiền tố].."
echo "tập tin renna s [hậu tố].."
echo "các tập tin renna r [biểu thức] [thay thế].."
lối ra 0
#xong!

Chương trình đổi tên file (đơn giản)
#!/bin/bash
#renames.sh
# chương trình đổi tên đơn giản

tiêu chí=$1
re_match=$2
thay thế=$3

Đối với tôi trong $(ls *$criteria*);
LÀM
src=$i
tgt=$(echo $i | sed -e "s/$re_match/$replace/")
mv $src $tgt
xong

Nếu điều xảy ra khác với điều bạn mong đợi (gỡ lỗi)

Làm cách nào tôi có thể gọi BASH?

Sẽ thật tuyệt nếu thêm vào dòng đầu tiên

#!/bin/bash -x
Điều này sẽ tạo ra một số thông tin đầu ra thú vị.

Về tài liệu

Bạn không nên ngần ngại sửa chữa, bổ sung hoặc bất cứ điều gì khác nếu theo ý kiến ​​​​của bạn, nó nên có trong tài liệu này. Tác giả sẽ cố gắng cập nhật nó bất cứ khi nào có thể.

Ống là một kênh một chiều để liên lạc giữa các quá trình. Thuật ngữ này được Douglas McIlroy đặt ra để chỉ vỏ bọc Unix và được đặt tên theo đường dẫn. Các đường ống thường được sử dụng nhiều nhất trong các tập lệnh shell để liên kết nhiều lệnh bằng cách chuyển hướng đầu ra của một lệnh (stdout) sang đầu vào (stdin) của lệnh tiếp theo, sử dụng ký hiệu ống '|':
cmd1 | cmd2 | .... | cmdN
Ví dụ:
$ grep -i “lỗi” ./log | wc -l 43
grep thực hiện tìm kiếm không phân biệt chữ hoa chữ thường cho chuỗi “lỗi” trong tập tin nhật ký, nhưng kết quả tìm kiếm không hiển thị trên màn hình mà được chuyển hướng đến đầu vào (stdin) của lệnh wc, từ đó đếm số dòng.

Logic

Đường dẫn cung cấp khả năng thực thi lệnh không đồng bộ bằng cách sử dụng bộ đệm I/O. Do đó, tất cả các nhóm trong quy trình đều làm việc song song, mỗi nhóm có quy trình riêng.

Kích thước bộ đệm kể từ phiên bản kernel 2.6.11 là 65536 byte (64Kb) và bằng một trang bộ nhớ trong các kernel cũ hơn. Khi cố gắng đọc từ bộ đệm trống, quá trình đọc sẽ chặn cho đến khi dữ liệu xuất hiện. Tương tự như vậy, nếu bạn cố gắng ghi vào bộ đệm đầy, quá trình ghi sẽ bị chặn cho đến khi dung lượng cần thiết được giải phóng.
Điều quan trọng là mặc dù thực tế là đường dẫn hoạt động trên các bộ mô tả tệp của luồng I/O, nhưng tất cả các thao tác đều được thực hiện trong bộ nhớ mà không tải lên đĩa.
Tất cả thông tin bên dưới là dành cho shell bash-4.2 và kernel 3.10.10.

Gỡ lỗi đơn giản

Tiện ích strace cho phép bạn theo dõi các cuộc gọi hệ thống trong quá trình thực hiện chương trình:
$ strace -f bash -c ‘/bin/echo foo | thanh grep' .... getpid() = 13726<– PID основного процесса... pipe() <– системный вызов для создания конвеера.... clone(....) = 13727 <– подпроцесс для первой команды конвеера (echo) ... execve("/bin/echo", ["/bin/echo", "foo"], ..... clone(....) = 13728 <– подпроцесс для второй команды (grep) создается так же основным процессом... stat("/home/aikikode/bin/grep", ... Видно, что для создания конвеера используется системный вызов pipe(), а также, что оба процесса выполняются параллельно в разных потоках.

Rất nhiều mã nguồn bash và kernel

Mã nguồn, cấp 1, shell

Vì tài liệu tốt nhất là mã nguồn nên hãy chuyển sang nó. Bash sử dụng Yacc để phân tích các lệnh đầu vào và trả về 'command_connect()' khi gặp ký tự '|'.
phân tích cú pháp.y :
Đường dẫn 1242: đường ống '|' newline_list đường ống 1243 ( $$ = command_connect ($1, $4, '|'); ) 1244 | đường ống BAR_AND newline_list đường ống 1245 ( 1246 /* Tạo cmd1 |& cmd2 tương đương với cmd1 2>&1 | cmd2 */ 1247 COMMAND *tc; 1248 REDIRECTEE rd, sd; 1249 REDIRECT *r; 1250 1251 tc = $1->type == cm_simple ? (COMMAND *)$1->value.Simple: $1; 1252 sd.dest = 2; 1253 rd.dest = 1; 1254 r = make_redirection (sd, r_duplicate_output, rd, 0); 1255 if (tc->redirects ) 1256 ( 1257 đăng ký REDIRECT *t; 1258 for (t = tc->redirects; t->next; t = t->next) 1259 ; 1260 t->next = r; 1261 ) 1262 khác 1263 tc->redirects = r; 1264 1265 $$ = command_connect ($1, $4, '|'); 1266 ) 1267 | lệnh 1268 ( $$ = $1; ) 1269 ; Cũng ở đây, chúng ta thấy quá trình xử lý cặp ký tự '|&', tương đương với việc chuyển hướng cả stdout và stderr vào đường dẫn. Tiếp theo, hãy xem command_connect():make_cmd.c :
194 LỆNH * 195 lệnh_connect (com1, com2, đầu nối) 196 LỆNH *com1, *com2; đầu nối int 197; 198 ( 199 KẾT NỐI *temp; 200 201 temp = (KẾT NỐI *)xmalloc (sizeof (KẾT NỐI)); 202 temp->connector = kết nối; 203 temp->first = com1; 204 temp->second = com2; 205 return ( make_command (cm_connection, (SIMPLE_COM *)temp)); 206 ) trong đó trình kết nối là ký tự '|' dưới dạng int. Khi một chuỗi lệnh (được liên kết qua '&', '|', ';', v.v.) được thực thi, exec_connection():execute_cmd.c được gọi:
2325 trường hợp '|': ... 2331 exec_result = exec_pipeline (lệnh, không đồng bộ, pipe_in, pipe_out, fds_to_close);
PIPE_IN và PIPE_OUT là các bộ mô tả tệp chứa thông tin về luồng đầu vào và đầu ra. Họ có thể lấy giá trị NO_PIPE, nghĩa là I/O là stdin/stdout.
exec_pipeline() là một hàm khá mở rộng, việc triển khai hàm này được chứa trong exec_cmd.c . Chúng tôi sẽ xem xét những phần thú vị nhất đối với chúng tôi.
exec_cmd.c :
2112 trước = pipe_in; 2113 cmd = lệnh; 2114 2115 while (cmd && cmd->type == cm_connection && 2116 cmd->value.Connection && cmd->value.Connection->connector == '|') 2117 ( 2118 /* Tạo một đường dẫn giữa hai lệnh */ 2119 if (ống (phim)< 0) 2120 { /* возвращаем ошибку */ } ....... /* Выполняем первую команду из конвейера, используя в качестве входных данных prev - вывод предыдущей команды, а в качестве выходных fildes - выходной mô tả tập tin, kết quả từ lệnh gọi pipe() */ 2178 exec_command_internal (cmd->value.Connection->first, asynchronous, 2179 prev, fildes, fd_bitmap); 2180 2181 if (trước >= 0) 2182 đóng (trước); 2183 2184 prev = phim; /* Đầu ra của chúng ta trở thành đầu vào cho lệnh tiếp theo */ 2185 close (fildes); ....... 2190 cmd = cmd->value.Connection->giây; /* “Di chuyển” tới lệnh tiếp theo từ đường ống */ 2191 ) Vì vậy bash xử lý ký hiệu đường ống bằng cách cuộc gọi hệ thống pipe() cho mỗi ký tự '|' gặp phải và thực thi từng lệnh trong một quy trình riêng biệt bằng cách sử dụng bộ mô tả tệp tương ứng làm luồng đầu vào và đầu ra.

Mã nguồn, cấp 2, hạt nhân

Hãy chuyển sang mã kernel và xem cách triển khai hàm pipe(). Bài viết này thảo luận về phiên bản kernel 3.10.10 ổn định.
(các phần mã không quan trọng đối với bài viết này sẽ bị bỏ qua):
/* Kích thước bộ đệm đường dẫn tối đa cho người dùng không có đặc quyền. Có thể được đặt bằng root trong tệp /proc/sys/fs/pipe-max-size */ 35 unsigned int pipe_max_size = 1048576; /* Kích thước tối thiểu của bộ đệm đường ống, theo khuyến nghị của POSIX, bằng kích thước của một trang bộ nhớ, tức là. 4Kb */ 40 unsigned int pipe_min_size = PAGE_SIZE; 869 int create_pipe_files(struct file **res, int flags) 870 ( 871 int err; 872 struct inode *inode = get_pipe_inode(); 873 struct file *f; 874 đường dẫn đường dẫn cấu trúc; 875 static struct qstr name = (. name = "" ); /* Phân bổ nha khoa trong dcache */ 881 path.dentry = d_alloc_pseudo(pipe_mnt->mnt_sb, &name); /* Phân bổ và khởi tạo cấu trúc tệp. Hãy chú ý đến FMODE_WRITE, cũng như cờ O_WRONLY, tức là cái này cấu trúc chỉ ghi và sẽ được sử dụng làm luồng đầu ra trong đường ống. Chúng ta sẽ quay lại cờ O_NONBLOCK sau. */ 889 f = alloc_file(&path, FMODE_WRITE, &pipefifo_fops); 893 f->f_flags = O_WRONLY | (flags & (O_NONBLOCK | O_DIRECT )); /* Tương tự, chúng ta phân bổ và khởi tạo cấu trúc tệp để đọc (xem FMODE_READ và cờ O_RDONLY) */ 896 res = alloc_file(&path, FMODE_READ, &pipefifo_fops); 902 res->f_flags = O_RDONLY | (flags & O_NONBLOCK); 903 res = f; 904 return 0; 917 ) 918 919 static int __do_pipe_flags(int *fd, struct file **files, int flags) 920 ( 921 lỗi int; 922 int fdw, fdr; /* Tạo cấu trúc tệp cho bộ mô tả tệp đường dẫn (xem chức năng ở trên) */ 927 error = create_pipe_files(files, flags); /* Chọn bộ mô tả tệp miễn phí */ 931 fdr = get_unused_fd_flags(flags); 936 fdw = get_unused_fd_flags(cờ); 941 kiểm toán_fd_pair(fdr, fdw); 942 fd = fdr; 943 fd = fdw; 944 trả về 0; 952 ) /* Thực hiện trực tiếp các hàm int pipe2(int pipefd, int flags)... */ 969 SYSCALL_DEFINE2(pipe2, int __user *, fildes, int, flags) 970 ( 971 tệp cấu trúc *files; 972 int fd; / * Chúng tôi tạo cấu trúc cho đầu vào/đầu ra và tìm kiếm các bộ mô tả miễn phí */ 975 __do_pipe_flags(fd, files, flags); /* Sao chép các bộ mô tả tệp từ không gian kernel sang không gian người dùng */ 977 copy_to_user(fildes, fd, sizeof(fd)) ; /* Chúng ta gán các bộ mô tả tệp cho các con trỏ cấu trúc */ 984 fd_install(fd, files); 985 fd_install(fd, files); 989 ) /* ...và int pipe(int pipefd), về cơ bản là một trình bao bọc cho gọi pipe2 bằng cờ mặc định; */ 991 SYSCALL_DEFINE1(pipe, int __user *, fildes) 992 ( 993 return sys_pipe2(fildes, 0); 994 ) Nếu bạn nhận thấy, mã sẽ kiểm tra cờ O_NONBLOCK. Nó có thể được đặt bằng thao tác F_SETFL trong fcntl. Nó chịu trách nhiệm chuyển sang chế độ mà không chặn các luồng I/O trong đường ống. Ở chế độ này, thay vì chặn, quá trình đọc/ghi vào luồng sẽ kết thúc với mã lỗi EAGAIN.

Kích thước tối đa của khối dữ liệu sẽ được ghi vào đường dẫn bằng một trang bộ nhớ (4Kb) đối với kiến ​​trúc nhánh:
:
8 #define PIPE_BUF PAGE_SIZE Đối với hạt nhân >= 2.6.35, bạn có thể thay đổi kích thước bộ đệm đường ống:
fcntl(fd, F_SETPIPE_SZ, ) Kích thước bộ đệm tối đa được phép, như chúng ta đã thấy ở trên, được chỉ định trong tệp /proc/sys/fs/pipe-max-size.

Mẹo & thủ thuật

Trong các ví dụ bên dưới, chúng tôi sẽ thực thi ls trên thư mục Tài liệu hiện có và hai tệp không tồn tại: ./non-being_file và . /other_không tồn tại_file.
  1. Chuyển hướng cả stdout và stderr sang pipe
    ls -d ./Documents ./không tồn tại_file ./other_non-tồn tại_file 2>&1 | egrep “Doc|other” ls: không thể truy cập ./other_non-exist_file: Không có tệp hoặc thư mục như vậy ./Documents hoặc bạn có thể sử dụng tổ hợp ký tự '|&' (bạn có thể tìm thấy điều này trong tài liệu shell (man bash) hoặc và từ mã nguồn ở trên, nơi chúng tôi đã phân tích cú pháp trình phân tích cú pháp bash Yacc):
    ls -d ./Documents ./non-being_file ./other_non-being_file |& egrep “Doc|other” ls: không thể truy cập ./other_non-being_file: Không có tập tin hoặc thư mục như vậy ./Documents
  2. Chuyển hướng _only_ stderr sang đường ống
    $ ls -d ./Documents ./không tồn tại_file ./other_non-tồn tại_file 2>&1 >/dev/null | egrep “Doc|other” ls: không thể truy cập ./other_non-being_file: Không có tập tin hoặc thư mục như vậy Tự bắn vào chân mình
    Điều quan trọng là phải tôn trọng thứ tự chuyển hướng stdout và stderr. Ví dụ: sự kết hợp '>/dev/null 2>&1' sẽ chuyển hướng cả stdout và stderr sang /dev/null.
  3. Lấy mã hoàn thành đường ống chính xác
    Theo mặc định, mã thoát đường ống là mã thoát của lệnh cuối cùng trong đường ống. Ví dụ: lấy lệnh ban đầu thoát ra với mã khác 0:
    $ ls -d ./không tồn tại_file 2>/dev/null; tiếng vang $? 2 Và đặt nó vào đường ống:
    $ ls -d ./không tồn tại_file 2>/dev/null | wc; tiếng vang $? 0 0 0 0 Bây giờ mã thoát đường ống là mã thoát lệnh wc, tức là. 0.

    Thông thường, chúng ta cần biết liệu có xảy ra lỗi trong quá trình thực hiện đường ống hay không. Để thực hiện việc này, hãy đặt tùy chọn pipefail, tùy chọn này cho shell biết rằng mã thoát đường ống sẽ khớp với mã thoát khác 0 đầu tiên của một trong các lệnh đường ống hoặc 0 nếu tất cả các lệnh hoàn thành chính xác:
    $ set -o pipefail $ ls -d ./non-exist_file 2>/dev/null | wc; tiếng vang $? 0 0 0 2 Tự bắn vào chân mình
    Bạn nên lưu ý đến các lệnh “vô hại” có thể trả về giá trị khác 0. Điều này không chỉ áp dụng khi làm việc với băng tải. Ví dụ: hãy xem xét ví dụ grep:
    $ egrep “^foo=+” ./config | awk '(print “new_”$0;)’ Ở đây chúng tôi in tất cả các dòng được tìm thấy, thêm ‘new_’ vào đầu mỗi dòng hoặc không in bất cứ thứ gì nếu không tìm thấy một dòng nào có định dạng bắt buộc. Vấn đề là grep không thành công với mã 1 nếu không tìm thấy kết quả khớp nào, vì vậy nếu tập lệnh của chúng tôi có tùy chọn pipefail được đặt, ví dụ này sẽ thất bại với mã 1:
    $ set -o pipefail $ egrep “^foo=+” ./config | awk ‘(print “new_”$0;)’ >/dev/null; tiếng vang $? 1 Trong các tập lệnh lớn có cấu trúc phức tạp và quy trình dài, điểm này có thể bị bỏ qua và dẫn đến kết quả không chính xác.

  4. Gán giá trị cho các biến trong một đường ống
    Trước tiên, hãy nhớ rằng tất cả các lệnh trong đường dẫn được thực thi trong các quy trình riêng biệt thu được bằng cách gọi clone(). Điều này thường không phải là vấn đề trừ khi giá trị biến thay đổi.
    Hãy xem xét ví dụ sau:
    $ a=aaa $ b=bbb $ echo “một hai” | đọc a b Bây giờ chúng ta mong đợi giá trị của biến a và b lần lượt là “một” và “hai”. Trên thực tế, chúng sẽ vẫn là “aaa” và “bbb”. Nói chung, bất kỳ thay đổi nào đối với giá trị của các biến trong đường dẫn bên ngoài nó sẽ khiến các biến không thay đổi:
    $ filefound=0 $ tìm . -loại f -size +100k | while true do read f echo “$f is over 100KB” filefound=1 break # thoát sau khi tìm thấy tệp đầu tiên $ echo $filefound; Ngay cả khi tìm thấy tệp lớn hơn 100Kb, cờ tìm thấy tệp vẫn sẽ được đặt thành 0.
    Có một số giải pháp khả thi cho vấn đề này:
    • sử dụng bộ -- $var
      Cấu trúc này sẽ đặt các biến vị trí theo nội dung của biến var. Ví dụ, như trong ví dụ đầu tiên ở trên:
      $ var=”one two” $ set -- $var $ a=$1 # “one” $ b=$2 # “two” Cần phải lưu ý rằng tập lệnh sẽ mất các tham số vị trí ban đầu mà nó được gọi .
    • chuyển tất cả logic để xử lý giá trị biến sang cùng một quy trình con trong đường ống:
      $ echo “một” | (đọc a; echo $a;) một
    • thay đổi logic để tránh gán các biến bên trong đường ống.
      Ví dụ: hãy thay đổi ví dụ tìm kiếm của chúng tôi:
      $ filefound=0 $ for f in $(find . -type f -size +100k) # chúng tôi đã xóa đường ống, thay thế nó bằng một vòng lặp do read f echo “$f is over 100KB” filefound=1 break done $ echo $ tìm thấy tập tin;
    • (chỉ dành cho bash-4.2 trở lên) sử dụng tùy chọn Lastpipe
      Tùy chọn Lastpipe hướng dẫn shell thực thi lệnh đường ống cuối cùng trong quy trình chính.
      $ (shopt -s Lastpipe; a=”aaa”; echo “one” | read a; echo $a) one Điều quan trọng là tùy chọn Lastpipe trên dòng lệnh phải được đặt trong cùng quy trình nơi đường ống tương ứng sẽ được đặt được gọi, vì vậy dấu ngoặc đơn trong ví dụ trên là bắt buộc. Trong script, dấu ngoặc đơn là tùy chọn.

Bảng cheat này bao gồm theo chủ đề: Giới thiệu về shell, điều hướng, các lệnh cơ bản, biến môi trường, trình kết nối, đường ống, chuyển hướng I/O, quyền và phím tắt.

Bash Shell: Giới thiệu

Shell, hay shell, là một chương trình, trong trường hợp của chúng tôi được gọi là “bash”, viết tắt của Bourne Again Shell. Shell chấp nhận các lệnh của bạn và chuyển chúng tới hệ điều hành. Để tương tác với hệ thống, các thiết bị đầu cuối được sử dụng, chẳng hạn như gnome-terminal, eterm, nxterm, v.v.

dẫn đường

TRONG Tập tin Linux và các thư mục có tổ chức phân cấp, tức là có một thư mục ban đầu nhất định gọi là thư mục gốc. Nó chứa các tập tin và thư mục con, từ đó chứa các tập tin và thư mục con của riêng chúng.

pwd

Lệnh pwd, viết tắt của in thư mục làm việc, hiển thị vị trí hiện tại trong cấu trúc thư mục.

đĩa CD

Lệnh cd cho phép bạn truy cập danh mục mới.

mkdir

Lệnh mkdir tạo một thư mục mới trong thư mục hiện tại.

Các lệnh cơ bản

người đàn ông

Lệnh man hiển thị hướng dẫn sử dụng lệnh. Ví dụ: lệnh sau sẽ hiển thị tất cả thông tin về lệnh mèo:

$ người đàn ông mèo

con mèo

Lệnh cat đọc tệp được truyền dưới dạng đối số và in nội dung của nó kênh tiêu chuẩnđầu ra. Truyền nhiều tệp làm đối số sẽ in nội dung được nối của tất cả các tệp.

tiếng vọng

Lệnh echo in các đối số của nó ra đầu ra tiêu chuẩn.

$ echo Xin chào thế giới Xin chào thế giới

Nếu bạn gọi echo mà không có đối số, một chuỗi trống sẽ được in ra.

cái đầu

Lệnh head đọc 10 dòng đầu tiên của bất kỳ văn bản nào được truyền vào và xuất chúng ra ống tiêu chuẩn. Số dòng hiển thị có thể thay đổi:

$head -50 test.txt

đuôi

Lệnh tail hoạt động tương tự lệnh head nhưng đọc các dòng từ cuối:

đuôi $ -50 test.txt

Bạn cũng có thể xem các dòng được thêm vào tệp trong thời gian thực bằng cờ -f:

$ tail -f test.txt

ít hơn

Lệnh less cho phép bạn điều hướng qua một tệp hoặc đoạn văn bản được truyền theo cả hai hướng.

$ ít test.txt $ ps aux | ít hơn

Tìm hiểu thêm về mục đích của biểu tượng | sẽ được đề cập bên dưới trong phần lịch sử của lệnh.

Phím tắt thông dụngSự miêu tả
GDi chuyển đến cuối tập tin
gDi chuyển đến đầu tập tin
:50 Di chuyển đến dòng 50 của tập tin
qThoát ít hơn
/thuật ngữ tìm kiếmTìm kiếm chuỗi khớp với 'thuật ngữ tìm kiếm' bên dưới dòng hiện tại
/
?thuật ngữ tìm kiếmTìm một dòng khớp với 'thuật ngữ tìm kiếm' phía trên dòng hiện tại
? Di chuyển đến kết quả tìm kiếm phù hợp tiếp theo
hướng lênDi chuyển lên một dòng
xuốngDi chuyển xuống một dòng
trang lênDi chuyển lên một trang
trang dướiDi chuyển một trang xuống

ĐÚNG VẬY

Lệnh true luôn trả về 0 làm trạng thái đầu ra để biểu thị thành công.

SAI

Lệnh sai luôn trả về trạng thái đầu ra khác 0 để biểu thị lỗi.

$?

$? là một biến chứa trạng thái đầu ra của lần chạy lệnh cuối cùng. Trạng thái thường đề cập đến mã trả về của chương trình. 0 có nghĩa là chương trình thực hiện thành công, bất kỳ giá trị nào lớn hơn 0 phản ánh thực tế rằng một số lỗi đã xảy ra trong quá trình thực thi. Nhân tiện, đây là lý do tại sao trong bash 0 được coi là đúng và mọi thứ không phải 0 đều sai:

$true$echo$? 0 $ sai $ tiếng vang $? 1

grep

Lệnh grep tìm kiếm chuỗi được truyền trong tệp được chỉ định:

$ cat user.txt người dùng:mật khẩu sinh viên:123 người dùng:mật khẩu giáo viên:321 $ grep "student` file1.txt người dùng:mật khẩu sinh viên:123

grep cũng có thể chấp nhận nhiều tập tin và biểu thức chính quyđể làm rõ định dạng văn bản.

lịch sử

Lệnh history hiển thị lịch sử dòng lệnh. Nó thường được sử dụng kết hợp với lệnh grep để tìm kiếm một lệnh cụ thể. Ví dụ: đoạn mã sau sẽ tìm tất cả các lệnh có chứa chuỗi g++ :

$lịch sử | grep g++ 155 g++ file1.txt 159 g++ file2.txt

Ký hiệu | cũng được sử dụng ở đây. - đây được gọi là băng tải (ống). Nhờ nó, bạn có thể chuyển hướng đầu ra của một lệnh sang đầu vào của lệnh khác - do đó, trong ví dụ trên, tất cả lịch sử thường được xuất ra bởi lệnh lịch sử trực tiếp đến đầu ra cuối cùng sẽ được chuyển hướng đến grep làm đầu vào. Chúng ta sẽ không thấy đầu ra của lệnh history, nhưng chúng ta sẽ thấy đầu ra của lệnh grep.

Điều này có thể khá khó hiểu nếu không thực hành, vì vậy hãy tự mình thử nghiệm các lệnh ls , history , ps (được mô tả bên dưới) để chuyển hướng đầu ra của chúng sang grep , sed hoặc less chẳng hạn.

xuất khẩu

Lệnh xuất đặt các biến môi trường để truyền cho các tiến trình con. Ví dụ: đây là cách bạn có thể truyền một biến tên với giá trị sinh viên:

$ tên xuất = sinh viên

ps

Lệnh ps hiển thị thông tin về tiến trình đang chạy.

$ ps PID TTY TIME CMD 35346 điểm/2 00:00:00 bash

Bốn yếu tố được đầu ra:

  • ID tiến trình (PID),
  • loại thiết bị đầu cuối (TTY),
  • thời gian vận hành quy trình (TIME),
  • tên của lệnh bắt đầu quá trình (CMD).

ôi

Lệnh awk tìm và thay thế văn bản trong tệp bằng cách sử dụng mẫu đã cho: awk "pattern (action)" test.txt

quên

Lệnh wget tải các tập tin từ Internet và đặt chúng vào thư mục hiện tại.

$ wget https://github.com/mikeizbicki/ucr-cs100

nc

ping

Lệnh ping kiểm tra kết nối mạng.

$ ping google.com PING google.com (74.125.224.34) 56(84) byte dữ liệu. 64 byte từ lax17s01-in-f2.1e100.net (74.125.224.34): icmp_req=1 ttl=57 time=7,82 ms --- thống kê ping của google.com --- 1 gói được truyền, 1 gói đã nhận, mất gói 0% , thời gian 8ms rtt min/avg/max/mdev = 7,794/8,422/10,792/0,699 ms

Số liệu thống kê ở cuối hiển thị số lượng kết nối được thực hiện trước khi lệnh hoàn thành và thời gian cần thiết để hoàn thành chúng.

git

Biến môi trường

Biến môi trường là các biến được đặt tên chứa các giá trị được sử dụng bởi một hoặc nhiều ứng dụng.

Biến PATH chứa danh sách các thư mục mà hệ thống tìm kiếm các tệp thực thi.

Biến HOME chứa đường dẫn đến thư mục chính của bạn người dùng hiện tại.

Đầu nối

Trình kết nối cho phép bạn chạy nhiều lệnh cùng một lúc.

$ true && echo Xin chào Xin chào $ false || echo Xin chào Xin chào $ echo Xin chào ; ls Xin chào test.txt file1.txt file2.txt

Băng tải

Băng tải hoặc đường ống cho phép bạn kết nối các kênh đầu vào và đầu ra nhiều đội khác nhau. Trong ví dụ sau, đầu ra của lệnh ls sẽ được chuyển đến head và kết quả là chỉ 10 phần tử đầu tiên được in.

$ ls -l | cái đầu

Chuyển hướng I/O

Chuyển hướng đầu ra

Các ký hiệu > và >> được sử dụng để chuyển hướng đầu ra tiêu chuẩn.

Ví dụ: mã này sẽ chuyển đầu ra ls sang một tệp thay vì ra màn hình:

$ ls > files.txt $ cat files.txt file1.cpp sample.txt

Nếu tệp không tồn tại thì nó sẽ được tạo và nếu nó tồn tại thì nó sẽ bị ghi đè. Để tránh ghi đè, bạn nên sử dụng lệnh >> - nó sẽ thêm dữ liệu vào cuối tệp.

Chuyển hướng đầu vào

Để chuyển hướng đầu ra tiêu chuẩn, hãy sử dụng ký hiệu< . В следующем примере sort берет входные данные из файла, а не с клавиатуры:

$ cat files.txt c b $ sắp xếp< files.txt b c

lệnh sắp xếp in nội dung của tệp ra màn hình vì chúng tôi không chuyển hướng đầu ra. Điều này có thể được thực hiện như thế này:

$sắp xếp< files.txt >file_sorted.txt

Chuyển hướng nâng cao

Việc thêm & vào > khiến cả đầu ra tiêu chuẩn và lỗi được chuyển hướng. Ví dụ: tệp test.cpp sẽ xuất dòng stdout thành cout và dòng stderr thành cerr .

$ g++ test.cpp $ ./a.out >& test.txt $ cat test.txt stdout stderr

Nếu bạn muốn xuất một bộ mô tả tệp cụ thể, bạn có thể gán số của nó cho > .

TênBộ mô tảSự miêu tả
stdin0 Đầu vào tiêu chuẩn
thiết bị xuất chuẩn1 Đầu ra tiêu chuẩn
lỗi chuẩn2 Đầu ra lỗi tiêu chuẩn

Ví dụ: để chuyển hướng stderr sang test.txt, bạn sẽ làm như sau:

$ g++ test.cpp $ ./a.out 2> thiết bị xuất chuẩn test.txt $ cat test.txt thiết bị xuất chuẩn

Quyền truy cập

Lệnh ls -l hiển thị rất nhiều thông tin về quyền của từng file:

chmod

Lệnh chmod thay đổi quyền của tệp. Dưới đây là các tổ hợp cờ điển hình để thay đổi quyền của người dùng cụ thể:

Bạn có thể gọi chmod kèm theo mô tả những việc cần làm trên một tệp cụ thể. Ký hiệu - có nghĩa là loại bỏ quyền, ký hiệu + có nghĩa là thêm. Ví dụ sau sẽ làm cho tệp có thể đọc và ghi được bởi chủ sở hữu và nhóm:

$ chmod ug+rw test.txt $ ls -l test.txt -rw-rw---- 1 nhóm người dùng 1097374 Ngày 26 tháng 1 2:48 test.txt

Ngoài ra, chmod có thể được sử dụng với số bát phân, trong đó 1 là sự hiện diện của quyền và 0 là sự vắng mặt:

Rwx = 111 = 7 rw- = 110 = 6 r-x = 101 = 5 r-- = 100 = 4

Lệnh tiếp theo sẽ hoạt động giống như lệnh trước.

Chủ đề này là chủ đề thứ 4 trong loạt bài “Ngôn ngữ lệnh thông dịch viên bash" Anh ta sẽ nói về các cấu trúc điều khiển của ngôn ngữ như các câu lệnh có điều kiện. Nhưng trước khi chuyển sang phần mô tả của chúng, cần phải xem xét một số sắc thái sẽ làm cho việc xem xét tài liệu dưới đây trở nên dễ hiểu hơn.
Đầu tiên chúng ta hãy xem danh sách lệnh là gì. Danh sách lệnh là một lệnh, đường dẫn hoặc chuỗi lệnh/ống dẫn được phân tách bằng một trong các toán tử sau: ";", "&&", "||", kết thúc bằng dấu chấm phẩy.
; - toán tử thực hiện tuần tự một số lệnh. Mỗi lệnh tiếp theo chỉ bắt đầu được thực thi sau khi lệnh trước đó đã hoàn thành (không quan trọng nó có thành công hay không);
&& - toán tử chỉ thực hiện lệnh sau khi lệnh trước đó được hoàn thành thành công;
|| - người vận hành chỉ thực hiện một lệnh sau khi lệnh trước đó được thực thi không chính xác.
Mã thành công là 0 và mã lỗi không bằng 0 (tùy thuộc vào loại lỗi). Không nên nhầm lẫn điều này với các ngôn ngữ lập trình thông thường, trong đó 1 tương tự với true và 0 tương tự với false.
Bây giờ chúng ta có thể bắt đầu xem xét trực tiếp các câu lệnh có điều kiện.

toán tử trường hợp

Cú pháp chung của câu lệnh case là:

giá trị trường hợp trong
mẫu1) danh sách1;;
mẫu2 | mẫu3) danh sách2;;
esac

Trình tự logic thực hiện câu lệnh case:
a) mẫu đầu tiên khớp với giá trị được tìm kiếm;
b) nếu tìm thấy, danh sách lệnh tương ứng sẽ được thực thi, kết thúc bằng ";;";
c) quyền kiểm soát được chuyển giao cho các báo cáo sau khi xây dựng trường hợp.
Mẫu và danh sách được phân tách bằng ký tự ")". Một danh sách lệnh có thể tương ứng với một số điều kiện, sau đó chúng phải được phân tách bằng ký hiệu “|”.
Trong các mẫu, bạn có thể sử dụng các ký hiệu “*”, “?”, “”, đã được thảo luận trong chủ đề thứ hai của loạt bài này. Với sự giúp đỡ của họ, bạn có thể thực hiện một lệnh hoạt động như mặc định trong câu lệnh chuyển đổi các ngôn ngữ như C, PHP.
Đây là một ví dụ về trường hợp sử dụng:
echo -n "[Universal Viewer] Chỉ định tên tệp: "; đọc Kiểu chữ "$File" trong *.jpg|*.gif|*.png) eog $File ;; *.pdf) chứng minh $File ;; *.txt) less $File ;; *.html) firefox $File ;; /dev/*) echo "Chà, đây là những tập tin đáng sợ." ;; *) echo "Được rồi, được rồi - không phổ biến lắm." echo "Tôi không quen với loại tập tin này. Tôi không biết cách xem nó." ;; esac
Một ví dụ khác về việc sử dụng cấu trúc trường hợp:
echo "Lỗi. Tôi nên gửi tin nhắn cho ai?" echo "Gửi sếp: b" echo "Gửi đồng nghiệp: c" echo "Gửi không ai: bất kỳ phím nào" đọc trường hợp trả lời $answer in b|B) mail –s "nhật ký lỗi" sếp< error.log;; c|C) mail –s "Help! error log" –c denis nick < error.log;; *) echo "error"; exit;; esac

Câu lệnh if có điều kiện

Cú pháp chung của câu lệnh if là:

nếu list1 thì
danh sách2

fi

Dấu ngoặc vuông ở đây biểu thị các cấu trúc tùy chọn. Trình tự logic thực hiện câu lệnh case:
a) list1 được thực thi;
b) nếu nó được thực thi mà không có lỗi thì list2 sẽ được thực thi. Ngược lại, list3 sẽ được thực thi và nếu nó hoàn thành mà không có lỗi thì list4 sẽ được thực thi. Nếu list3 cũng trả về mã lỗi, list5 sẽ được thực thi;
c) quyền điều khiển được chuyển giao cho các toán tử theo cấu trúc if.
Đây là một ví dụ về việc sử dụng nếu:
nếu grep -q Bash file thì echo "Tệp chứa ít nhất một từ Bash." fi
Khi if và then xuất hiện trên cùng một dòng, cấu trúc if và then phải kết thúc bằng dấu chấm phẩy. Ví dụ:
$ nếu [$? –ne 0 ]; sau đó lặp lại “Lỗi”; fi
Bây giờ, biết rằng có thể đặt if và then trên cùng một dòng, hãy viết lại ví dụ trên:
nếu tệp grep -q Bash; sau đó echo "Tệp chứa từ Bash." fi

Câu lệnh kiểm tra và biểu thức điều kiện

Trong ví dụ trên, kiểm tra điều kiện được sử dụng thay vì phân tích mã thoát. Hai hình thức kiểm tra này tương đương nhau: lệnh kiểm tra tích hợp và [điều kiện]. Ví dụ: để kiểm tra sự tồn tại của tệp bạn cần viết:
kiểm tra –e<файл>
hoặc
[ -e<файл> ]
Nếu sử dụng dấu ngoặc vuông thì chúng phải cách nhau bằng dấu cách, vì "[" là tên của lệnh và "]" là đối số cuối cùng bắt buộc để hoàn thành lệnh.
Nếu điều kiện được kiểm tra thành công, nó sẽ trả về 0 và nếu sai, mã lỗi 1 sẽ được trả về.
Lệnh kiểm tra có thể kiểm tra xem một chuỗi có trống không. Một chuỗi không trống dẫn đến mã thoát là 0. Trống tương ứng – 1. Ví dụ:
$kiểm tra $USER; tiếng vang $?
Thiết kế "" phổ biến hơn so với "". Đây là phiên bản mở rộng của lệnh kiểm tra. Trong cấu trúc này, không có sự diễn giải bổ sung nào về tên tệp và các đối số không được chia thành các từ riêng biệt nhưng được phép thay thế các tham số và lệnh. Ví dụ:
file=/etc/passwd nếu [[ -e $file ]] thì echo “Đã tìm thấy tệp mật khẩu.” fi
Cấu trúc "" được ưu tiên hơn "" vì nó sẽ giúp tránh một số lỗi logic. Ví dụ: các toán tử "&&", "||", "<" и ">" bên trong " " hoàn toàn có thể chấp nhận được, trong khi bên trong " " lại tạo ra thông báo lỗi.
Cấu trúc "(())" cho phép bạn đánh giá các biểu thức số học bên trong nó. Nếu kết quả tính toán bằng 0 thì mã lỗi sẽ được trả về. Kết quả của phép tính khác 0 sẽ tạo ra mã trả về là 0. Nghĩa là, hoàn toàn trái ngược với hướng dẫn kiểm tra và "" đã thảo luận ở trên.
Câu lệnh if cho phép kiểm tra lồng nhau:
if echo "*if* tiếp theo nằm trong *if* đầu tiên." nếu [[ $comparison = "số nguyên" ]] thì ((a< b)) else [[ $a < $b ]] fi then echo "$a меньше $b" fi

Các biểu thức điều kiện có thể được kết hợp bằng các phép toán logic thông thường:
! <выражение>– phủ nhận;
<выражение1>-Một<выражение2>– logic VÀ;
<выражение1>–o<выражение2>– logic HOẶC.

Biểu thức điều kiện cơ bản cho tệp:
-e - tập tin tồn tại;
-f - tập tin thông thường(không phải thư mục hoặc tập tin thiết bị);
-s - kích thước tệp khác không;
-d - file là một thư mục;
-b - tệp là một thiết bị khối (đĩa mềm, cdrom, v.v.);
-c - tệp là thiết bị ký tự (bàn phím, modem, card âm thanh, v.v.);
-p - tập tin là một kênh;
-h - tập tin là một liên kết tượng trưng;
-L - tập tin là một liên kết tượng trưng;
-S - tập tin là một ổ cắm;
-t - tệp được liên kết với thiết bị đầu cuối;
-r - tệp có thể đọc được (đối với người dùng đã khởi chạy tập lệnh);
-w - tệp có thể ghi được (đối với người dùng đã khởi chạy tập lệnh);
-x - tệp có sẵn để thực thi (đối với người dùng đã khởi chạy tập lệnh);
-g - cờ (sgid) cho tập tin hoặc thư mục được đặt;
-u - cờ (suid) cho tệp được đặt;
-k - cờ bit dính được đặt;
-O - ​​​​bạn là chủ sở hữu của tập tin;
-G - bạn thuộc cùng nhóm với tệp;
-N - tệp đã được sửa đổi kể từ lần đọc cuối cùng;
file1 -nt file2 – file1 mới hơn file2;
file1 -ot file2 – file1 cũ hơn file2;
file1 -ef file2 – file1 và file2 là các liên kết “cứng” đến cùng một tệp.

Biểu thức điều kiện cơ bản để so sánh chuỗi:
-z string – độ dài chuỗi là 0;
-n string – độ dài chuỗi không bằng 0;
line1 == line2 – các dòng khớp nhau (tương tự như “=”);
line1 !== line2 – các dòng không khớp nhau (tương tự như “!=”);
dòng 1< строка2 – строка1 предшествует строке2 в лексикографическом порядке;
dòng1 > dòng2 – dòng1 theo sau dòng2 theo thứ tự từ điển.
Một biểu thức điều kiện số học có dạng:
đối số1 hoạt động đối số2, trong đó các đối số là số nguyên và các hoạt động sau được phép:
-eq – bằng nhau;
-ne – không bằng;
-lt – ít hơn;
-le – nhỏ hơn hoặc bằng;
-gt – thêm;
-ge – lớn hơn hoặc bằng;
< - меньше (внутри двойных dấu ngoặc đơn);
<= - меньше или равно (внутри двойных круглых скобок);
> - lớn hơn (trong ngoặc kép);
>= - lớn hơn hoặc bằng (trong ngoặc kép).

Hãy viết lại ví dụ trước bằng câu lệnh if:
echo "Lỗi. Tôi nên gửi tin nhắn cho ai?" echo "Sếp: b" echo "Đồng nghiệp: c" echo "Không có ai: bất kỳ chìa khóa nào" đọc câu trả lời if [ "$answer" == "b" –o "$answer" == "B" ]; rồi mail –s "nhật ký lỗi" sếp< error.log; elif [ "$answer" == "c" –o "$answer" == "C" ]; then mail –s "Help! error log" –c denis nick < error.log; else echo "error"; exit; fi

Trong chủ đề tiếp theo tôi sẽ tiếp tục xem xét các cấu trúc điều khiển của trình thông dịch lệnh bash. Cụ thể, các toán tử vòng lặp sẽ được xem xét. Và bây giờ tôi đang chờ ý kiến ​​​​và phê bình :).

CẬP NHẬT: Cảm ơn người dùng

Toán tử for-in nhằm mục đích truy cập tuần tự vào các giá trị được liệt kê trong danh sách. Mỗi giá trị lần lượt trong danh sách được gán cho một biến. Cú pháp như sau:

đối với biến trong value_list, thực hiện các lệnh

Hãy xem một ví dụ nhỏ:

#!/bin/bash cho i trong 0 1 2 3 4 #chúng ta sẽ lần lượt gán các giá trị từ 0 đến 4 cho biến $i làm echo "Số bảng điều khiển là $i ">> /dev/pts/$i #Viết dòng "Số bảng điều khiển là $i" vào tệp /dev/pts/$i (tệp thiết bị đầu cuối ảo) done #loop đã hoàn thành thoát 0

Sau khi chạy ví dụ, một dòng có số của nó sẽ xuất hiện trong 5 bảng điều khiển ảo (thiết bị đầu cuối) đầu tiên. Các giá trị từ danh sách được thay thế luân phiên vào biến $i và giá trị của biến này được xử lý theo vòng lặp.

Chu kỳ. trong khi lặp lại

Vòng lặp while phức tạp hơn vòng lặp for-in và được sử dụng để lặp lại các lệnh miễn là một số biểu thức còn đúng (mã trả về = 0). Cú pháp toán tử như sau:

while biểu thức hoặc lệnh trả về mã trả về của lệnh do xong

Hãy xem ví dụ sau về cách hoạt động của vòng lặp:

#!/bin/bash lại =có #gán lại giá trị "có" cho biến trong khi [" $lại"= "có"] #Chúng tôi sẽ lặp lại cho đến khi $again bằng "có" do echo "Xin vui lòng nhập tên:" đọc tên echo "Tên bạn đã nhập là $name" echo "Bạn có muốn tiếp tục không?" đọc lại xong echo "Tạm biệt"

Và bây giờ là kết quả của script:

Ite@ite-desktop:~$ ./bash2_primer1.sh Vui lòng nhập tên: ite Tên bạn đã nhập là it Bạn có muốn tiếp tục không? vâng Vui lòng nhập tên: mihail Tên bạn đã nhập là mihail Bạn có muốn tiếp tục không? không, tạm biệt

Như bạn có thể thấy, vòng lặp chạy cho đến khi chúng ta nhập nội dung nào đó khác ngoài “có”. Giữa do và done bạn có thể mô tả bất kỳ cấu trúc, toán tử, v.v., tất cả chúng sẽ được thực thi trong một vòng lặp. Nhưng bạn nên cẩn thận với vòng lặp này, nếu bạn chạy bất kỳ lệnh nào trong đó mà không thay đổi biến biểu thức, bạn có thể nhận được bị cuốn vào một vòng lặp vô tận.

Bây giờ về điều kiện sự thật. Sau một khoảng thời gian, như trong câu lệnh điều kiện if-then-else, bạn có thể chèn bất kỳ biểu thức hoặc lệnh nào trả về mã trả về và vòng lặp sẽ được thực thi cho đến khi mã trả về = 0! Toán tử "[" là một dạng tương tự của lệnh kiểm tra, kiểm tra tính đúng đắn của điều kiện được truyền cho nó.

Hãy xem một ví dụ khác, tôi lấy nó từ cuốn sách Advanced Bash scripting. Tôi thực sự thích nó Smile, nhưng tôi đã đơn giản hóa nó một chút. Trong ví dụ này, chúng tôi sẽ giới thiệu một loại vòng lặp khác, UNTIL-DO. Đây gần như là một dạng tương tự hoàn toàn của vòng lặp WHILE-DO, chỉ có điều nó được thực thi khi một số biểu thức sai.

Đây là một ví dụ:

#!/bin/bash echo "Nhập tử số:"đọc tiếng vang cổ tức "Nhập mẫu số:"đọc số chia dnd =$dividend #chúng ta sẽ thay đổi các biến số bị chia và số chia, #hãy lưu giữ kiến ​​thức của mình vào các biến khác, bởi vì... Họ cho chúng tôi#sẽ cần dvs =$số chia còn lại =1 cho đến [ " $ còn lại "-eq 0 ] hãy để "số dư = số chia % cổ tức" cổ tức =$ số chia số chia =$ số dư được thực hiện echo "GCD của các số $dnd và $dvs = $dividend "

Kết quả thực thi script:

Ite@ite-desktop:~$ ./bash2_primer3.sh Nhập tử số: 100 Nhập mẫu số: 90 GCD của 100 và 90 = 10

Các hoạt động toán học

hãy ra lệnh.

Lệnh let thực hiện các phép tính số học trên các số và biến.

Hãy xem một ví dụ nhỏ trong đó chúng tôi thực hiện một số phép tính trên các số đã nhập:

#!/bin/bash echo "Nhập a: " đọc echo "Nhập b: " read b let "c = a + b" #addition echo "a+b= $c"để "c = a / b" #division echo "a/b= $c" hãy để "c<<= 2" #shift c 2 bit sang trái tiếng vọng "c sau khi dịch chuyển 2 bit: $c "đặt "c = a % b" # tìm phần dư của a chia cho b tiếng vang " $a / $b . phần còn lại: $c "

Kết quả thực hiện:

Ite@ite-desktop:~$ ./bash2_primer2.sh Nhập a: 123 Nhập b: 12 a+b= 135 a/b= 10 c sau khi dịch 2 chữ số: 40 123 / 12. số dư: 3

Chà, như bạn có thể thấy, không có gì phức tạp cả, danh sách các phép toán là tiêu chuẩn:

Phép cộng
- - phép trừ
* - phép nhân
/ - phân công
** - lũy thừa
% - mô đun (phân chia theo mô đun), phần dư của phép chia

let cho phép bạn sử dụng chữ viết tắt cho các lệnh số học, do đó làm giảm số lượng biến được sử dụng.

Ví dụ: a = a+b tương đương với a +=b, v.v.

Làm việc với các chương trình bên ngoài khi viết tập lệnh shell

Đầu tiên, một số lý thuyết hữu ích.

Chuyển hướng luồng

Bash (giống như nhiều shell khác) có các bộ mô tả tệp tích hợp: 0 (stdin), 1 (stdout), 2 (stderr).

thiết bị xuất chuẩn - Đầu ra tiêu chuẩn. Mọi thứ mà chương trình xuất ra đều ở đây
stdin - Đầu vào tiêu chuẩn. Đây là tất cả những gì người dùng gõ vào bảng điều khiển
stderr - Đầu ra lỗi tiêu chuẩn.

Đối với các thao tác với các tay cầm này, có các ký tự đặc biệt: > (chuyển hướng đầu ra),< (перенаправление ввода). Оперировать ими не сложно. Например:

mèo/dev/ngẫu nhiên >/dev/null

chuyển hướng đầu ra của cat /dev/random sang /dev/null (một thao tác hoàn toàn vô dụng) hoặc

ls -la > danh sách

ghi nội dung của thư mục hiện tại vào tệp danh sách (hữu ích hơn)

Nếu có nhu cầu nối vào một tập tin (khi sử dụng ">" nó được thay thế), bạn phải sử dụng ">>" thay vì ">"

sudo< my_password

sau khi hỏi sudo mật khẩu, nó sẽ được lấy từ tệp my_password, như thể bạn đã nhập nó từ bàn phím.

Nếu bạn chỉ cần ghi vào tệp các lỗi có thể xảy ra khi chạy chương trình, bạn có thể sử dụng:

./ chương trình_with_error 2 > error_file

số 2 trước ">" có nghĩa là bạn cần chuyển hướng mọi thứ kết thúc bằng bộ mô tả 2 (stderr).

Nếu bạn cần buộc stderr ghi vào thiết bị xuất chuẩn, thì việc này có thể được thực hiện như sau. đường:

./ chương trình_with_error 2 >& 1

ký hiệu "&" có nghĩa là một con trỏ tới bộ mô tả 1 (thiết bị xuất chuẩn)

(Theo mặc định, stderr ghi vào bảng điều khiển nơi người dùng đang làm việc (hay đúng hơn là ghi vào màn hình)).

2. Băng tải

Đường ống là một công cụ rất mạnh để làm việc với bảng điều khiển Bash. Cú pháp rất đơn giản:

đội1 | lệnh 2 - có nghĩa là đầu ra của lệnh 1 sẽ được chuyển làm đầu vào cho lệnh 2

Các đường ống có thể được nhóm thành chuỗi và xuất ra bằng cách sử dụng chuyển hướng đến một tệp, ví dụ:

ls -la | grep "băm" | sắp xếp > sắp xếp_list

Đầu ra của lệnh ls -la được chuyển tới lệnh grep, lệnh này chọn tất cả các dòng chứa từ băm và chuyển nó tới lệnh sắp xếp, lệnh này ghi kết quả vào tệpsort_list. Mọi thứ khá rõ ràng và đơn giản.

Thông thường, các tập lệnh Bash được sử dụng để tự động hóa một số thao tác thông thường trong bảng điều khiển, do đó đôi khi cần xử lý thiết bị xuất chuẩn của một lệnh và chuyển nó sang stdin sang lệnh khác, trong khi kết quả thực hiện một lệnh phải được xử lý trong một số lệnh. đường. Trong phần này tôi sẽ cố gắng giải thích các nguyên tắc cơ bản khi làm việc với các lệnh bên ngoài bên trong một tập lệnh. Tôi nghĩ rằng tôi đã đưa ra đủ ví dụ và bây giờ tôi chỉ có thể viết những điểm chính.

1. Truyền đầu ra cho một biến

Để ghi đầu ra của một lệnh vào một biến, chẳng hạn, chỉ cần đặt lệnh đó trong dấu ngoặc kép ``

A = ` echo "qwerty" ` echo $a

Kết quả: qwerty

Tuy nhiên, nếu bạn muốn lưu trữ danh sách các thư mục trong một biến, bạn phải xử lý đúng kết quả để đặt dữ liệu vào biến đó. Hãy xem một ví dụ nhỏ:

LIST =` find / svn/ -type d 2>/ dev/ null| awk "(FS="/") (in $4)" | sắp xếp | duy nhất | tr "\n" " " ` cho ONE_OF_LIST trong $LIST thực hiện svnadmin hotcopy / svn/ $ONE_OF_LIST / svn/ temp4backup/ $ONE_OF_LIST xong

Ở đây chúng tôi sử dụng vòng lặp for-do-done để lưu trữ tất cả các thư mục trong thư mục /svn/ bằng lệnh svnadmin hotcopy (trong trường hợp của chúng tôi không quan trọng, chỉ là một ví dụ). Dòng được quan tâm nhất là: LIST=`find /svn/ -type d 2>/dev/null| awk "(FS="/") (in $4)"| sắp xếp|uniq | tr "\n" " "` Trong đó biến LIST được giao nhiệm vụ thực thi lệnh find, được xử lý bởi các lệnh awk,sort, uniq, tr (chúng ta sẽ không xem xét tất cả các lệnh này, vì đây là một bài viết riêng) . Biến LIST sẽ chứa tên của tất cả các thư mục trong thư mục /svn/ được đặt trên một dòng (để đưa nó vào chu trình.

Như bạn có thể thấy, mọi thứ không khó, chỉ cần hiểu nguyên tắc và viết một vài đoạn script của riêng bạn. Để kết thúc bài viết, tôi xin chúc các bạn may mắn khi học BASH và Linux nói chung. Những lời chỉ trích, như thường lệ, được hoan nghênh. Bài viết tiếp theo có thể được dành cho việc sử dụng các chương trình như sed, awk.