Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Lập Trình Bash

Mẹo Bash Cần Thiết Cho Năng Suất Dòng Lệnh Hàng Ngày

Là shell mặc định cho nhiều biến thể Linux và Unix, Bash bao gồm nhiều tính năng ít được dùng, nên khó quyết định thảo luận gì. Cuối cùng, tôi quyết định tập trung vào mẹo Bash làm cho hoạt động hàng ngày dễ dàng hơn.

Là tư vấn viên, tôi thấy nhiều môi trường và phong cách làm việc đa dạng. Tôi lấy kinh nghiệm này để thu hẹp mẹo thành bốn danh mục rộng: Mẹo terminal và dòng lệnh, điều hướng và tệp, lịch sử, và lệnh hữu ích. Các danh mục này hoàn toàn tùy ý và phục vụ hơn để tổ chức suy nghĩ của tôi hơn là bất kỳ phân loại xác định nào. Nhiều mẹo ở đây có thể chủ quan phù hợp với nhiều danh mục hơn.

Không nói thêm, đây là một số mẹo Bash hữu ích nhất tôi từng gặp.

Làm Việc Với Lịch Sử Bash

Một trong những cách tốt nhất để tăng năng suất là học dùng lịch sử Bash hiệu quả hơn. Với ý nghĩ đó, có lẽ một trong những điều chỉnh quan trọng nhất bạn có thể làm trong môi trường đa người dùng là bật tùy chọn histappend cho shell. Để làm vậy, chỉ cần chạy lệnh sau:

shopt -s histappend

Điều này cho phép nhiều phiên terminal ghi vào lịch sử cùng lúc. Trong hầu hết môi trường tùy chọn này không được bật. Nghĩa là lịch sử thường bị mất nếu bạn mở hơn một phiên Bash (cục bộ hoặc qua SSH).

Một tác vụ phổ biến khác là lặp lại lệnh cuối với sudo. Ví dụ, giả sử bạn muốn tạo thư mục mkdir /etc/ansible/facts.d. Trừ khi bạn là root, lệnh này sẽ thất bại. Từ quan sát của tôi, hầu hết người dùng nhấn mũi tên up, cuộn đến đầu dòng, và thêm lệnh sudo. Có cách dễ hơn. Chỉ cần chạy lệnh như sau:

sudo !!

Bash sẽ chạy sudo rồi toàn bộ lệnh trước đó. Đây là chính xác trông như thế nào khi chạy liên tiếp:

[user@centos ~]$ mkdir -p /etc/ansible/facts.d
mkdir: cannot create directory '/etc/ansible': Permission denied

[user@centos ~]$ sudo !!
sudo mkdir -p /etc/ansible/facts.d

Khi !! chạy, toàn bộ lệnh được echo ra terminal để bạn biết điều gì vừa được thực thi.

Tương tự nhưng ít dùng hơn là phím tắt !*. Điều này nói với Bash bạn muốn tất cả đối số từ lệnh trước được lặp lại trong lệnh hiện tại. Điều này có thể hữu ích cho lệnh có nhiều đối số bạn muốn tái sử dụng. Ví dụ đơn giản là tạo một đống tệp rồi thay đổi quyền trên chúng:

[user@centos tmp]$ touch file1 file2 file3 file4
[user@centos tmp]$ chmod 777 !*
chmod 777 file1 file2 file3 file4

Nó chỉ tiện trong một bộ hoàn cảnh cụ thể, nhưng có thể tiết kiệm một số phím bấm.

Nói về tiết kiệm phím bấm, hãy nói về tìm lệnh trong lịch sử. Hầu hết người dùng sẽ làm kiểu:

history | grep 

Tuy nhiên, có cách dễ hơn tìm kiếm lịch sử. Nếu bạn nhấn

ctrl + r

Bash sẽ tìm kiếm ngược lịch sử của bạn. Khi bạn bắt đầu gõ, kết quả sẽ bắt đầu xuất hiện. Ví dụ:

(reverse-i-search)`hist': shopt -s histappend

Trong ví dụ trên, tôi gõ hist và nó khớp lệnh shopt chúng ta nói trước. Nếu bạn tiếp tục nhấn ctrl + r, Bash sẽ tiếp tục tìm ngược qua tất cả các khớp khác.

Mẹo cuối không phải mẹo bằng một lệnh hữu ích bạn có thể dùng để đếm và hiển thị lệnh thường dùng nhất trong lịch sử.

[user@centos tmp]$ history | awk 'BEGIN {FS="[ \t]+|\\|"} {print $3}' | sort | uniq -c | sort -nr | head
81 ssh
50 sudo
46 ls
45 ping
39 cd
29 nvidia-xrun
20 nmap
19 export

Trong ví dụ này, bạn thấy ssh là lệnh dùng nhiều nhất trong lịch sử của tôi lúc này.

Điều Hướng Và Đặt Tên Tệp

Bạn có thể đã biết nếu gõ lệnh, tên tệp, hoặc tên thư mục, bạn có thể nhấn phím tab một lần để hoàn thành từ cho bạn. Điều này hoạt động nếu có một khớp chính xác đơn lẻ. Tuy nhiên, bạn có thể không biết nếu nhấn tab hai lần, nó sẽ hiển thị tất cả khớp dựa trên thứ bạn đã gõ. Ví dụ:

[user@centos tmp]$ cd /lib 
lib/ lib64/

Điều này rất hữu ích cho điều hướng hệ thống tệp. Mẹo hữu ích khác là bật cdspell trong shell. Bạn có thể làm điều này bằng cách chạy lệnh shopt -s cdspush. Điều này sẽ giúp sửa lỗi chính tả:

[user@centos etc]$ cd /tpm
/tmp
[user@centos tmp]$ cd /ect
/etc

Nó không hoàn hảo, nhưng mỗi chút đều giúp!

Khi bạn đã đổi thư mục thành công, nếu bạn cần quay lại thư mục trước? Điều này không vấn đề nếu bạn không sâu trong cây thư mục. Nhưng nếu bạn ở đường dẫn khá sâu, như /var/lib/flatpak/exports/share/applications/, bạn có thể gõ:

cd /va/lib/fla/ex/sh/app

May mắn thay, Bash nhớ thư mục trước, và bạn có thể quay lại bằng cách chỉ gõ cd -. Đây là trông như thế nào:

[user@centos applications]$ pwd
/var/lib/flatpak/exports/share/applications

[user@centos applications]$ cd /tmp
[user@centos tmp]$ pwd
/tmp

[user@centos tmp]$ cd -
/var/lib/flatpak/exports/share/applications

Đó là ổn, nhưng nếu bạn có một đống thư mục muốn điều hướng dễ dàng? Bash lo cho bạn. Có biến bạn có thể thiết lập giúp điều hướng hiệu quả hơn. Ví dụ:

[user@centos applications]$ export CDPATH='~:/var/log:/etc'
[user@centos applications]$ cd hp
/etc/hp

[user@centos hp]$ cd Downloads
/home/user/Downloads

[user@centos Downloads]$ cd ansible
/etc/ansible

[user@centos Downloads]$ cd journal
/var/log/journal

Trong ví dụ trên, tôi đặt thư mục home (chỉ bằng tilde: ~), /var/log và /etc. Bất cứ thứ gì ở cấp cao nhất của các thư mục này sẽ được tự động điền khi bạn tham chiếu chúng. Thư mục không ở gốc của thư mục liệt kê trong CDPATH sẽ không tìm thấy. Nếu, ví dụ, thư mục bạn muốn là /etc/ansible/facts.d/ thì sẽ không hoàn thành bằng cách gõ cd facts.d. Điều này vì trong khi thư mục ansible tìm thấy dưới /etc, facts.d không. Do đó, CDPATH hữu ích để đến đỉnh cây bạn thường truy cập, nhưng có thể rườm rà khi duyệt cấu trúc thư mục lớn.

Cuối cùng, hãy nói về hai trường hợp phổ biến mọi người làm: Thay đổi phần mở rộng tệp và đổi tên tệp. Lúc đầu nghe có vẻ giống nhau, nhưng Bash cung cấp vài mẹo khác nhau để làm các tác vụ này.

Mặc dù có thể là thao tác "thô và bẩn", hầu hết người dùng lúc nào đó cần tạo bản sao nhanh của tệp đang làm. Hầu hết sẽ sao chép tên tệp chính xác và chỉ thêm phần mở rộng như .old hoặc .bak. Có phím tắt nhanh cho điều này trong Bash. Giả sử bạn có tên tệp như spideroak_inotify_db.07pkh3 bạn muốn giữ bản sao. Bạn có thể gõ:

cp spideroak_inotify_db.07pkh3 spideroak_inotify_db.07pkh3.bak

Bạn có thể làm nhanh điều này bằng thao tác copy/paste, dùng tab complete, có thể dùng phím tắt lặp lại đối số, hoặc chỉ gõ toàn bộ. Tuy nhiên, lệnh dưới nên chứng minh nhanh hơn khi bạn quen gõ nó:

cp spideroak_inotify_db.07pkh3{,.old}

Điều này (như bạn đoán) sao chép tệp bằng cách thêm phần mở rộng .old vào tệp. Tuyệt, bạn có thể nói, nhưng tôi muốn đổi tên nhiều tệp cùng lúc. Chắc, bạn có thể viết vòng lặp for để xử lý (và thực tế tôi thường làm vậy cho việc phức tạp) nhưng tại sao không khi có tiện ích tiện lợi tên rename? Có sự khác biệt dùng tiện ích này giữa Debian/Ubuntu và CentOS/Arch. rename dựa trên Debian dùng cú pháp kiểu SED:

user@ubuntu-1604:/tmp$ for x in `seq 1 5`; do touch old_text_file_${x}.txt; done

user@ubuntu-1604:/tmp$ ls old_text_file_*
old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt
old_text_file_2.txt old_text_file_4.txt

user@ubuntu-1604:/tmp$ rename 's/old_text_file/shiney_new_doc/' *.txt

user@ubuntu-1604:/tmp$ ls shiney_new_doc_*
shiney_new_doc_1.txt shiney_new_doc_3.txt shiney_new_doc_5.txt
shiney_new_doc_2.txt shiney_new_doc_4.txt

Trên CentOS hoặc Arch sẽ trông tương tự:

[user@centos /tmp]$ for x in `seq 1 5`; do touch old_text_file_${x}.txt; done

[user@centos /tmp]$ ls old_text_file_*
old_text_file_1.txt old_text_file_3.txt old_text_file_5.txt
old_text_file_2.txt old_text_file_4.txt

[user@centos tmp]$ rename old_text_file centos_new_doc *.txt

[user@centos tmp]$ ls centos_new_doc_*
centos_new_doc_1.txt centos_new_doc_3.txt centos_new_doc_5.txt
centos_new_doc_2.txt centos_new_doc_4.txt

Phím Tắt Bash (Key Bindings)

Bash có nhiều phím tắt bàn phím tích hợp. Bạn có thể tìm danh sách bằng cách gõ bind -p. Tôi nghĩ sẽ hữu ích làm nổi bật một vài cái, mặc dù một số có thể nổi tiếng.

  • ctrl + _ (hoàn tác/undo)
  • ctrl + t (hoán đổi hai ký tự)
  • ALT + t (hoán đổi hai từ)
  • ALT + . (in đối số cuối từ lệnh trước)
  • ctrl + x + * (mở rộng glob/sao)
  • ctrl + mũi tên (di chuyển tiến một từ)
  • ALT + f (di chuyển tiến một từ)
  • ALT + b (di chuyển lùi một từ)
  • ctrl + x theo sau ctrl + e (mở chuỗi lệnh trong editor để chỉnh sửa trước khi thực thi)
  • ctrl + e (di chuyển con trỏ đến cuối)
  • ctrl + a (di chuyển con trỏ đến đầu)
  • ctrl + xx (di chuyển đến đầu/cuối đối diện của dòng)
  • ctrl + u (cắt mọi thứ trước con trỏ)
  • ctrl + k (cắt mọi thứ sau con trỏ)
  • ctrl + y (dán từ bộ nhớ đệm)
  • ctrl + l (L thường, xóa màn hình)

Tôi sẽ không thảo luận cái rõ ràng hơn. Tuy nhiên, một số phím tắt hữu ích nhất tôi thấy là những cái cho phép xóa từ (hoặc đoạn văn bản) và hoàn tác chúng. Giả sử bạn định dừng một đống dịch vụ dùng systemd, nhưng chỉ muốn khởi động vài cái sau khi thao tác xong. Bạn có thể làm kiểu:

systemctl stop httpd mariadb nfs smbd

Nhưng nếu bạn xóa thừa một? Không vấn đề – chỉ dùng ctrl + _ để hoàn tác chỉnh sửa cuối.

Các lệnh cắt khác cho phép nhanh chóng xóa mọi thứ từ con trỏ đến cuối hoặc đầu dòng (dùng Ctrl + k và Ctrl + u, tương ứng). Điều này có lợi ích thêm là đặt văn bản cắt vào bộ nhớ đệm terminal để bạn dán sau (dùng ctrl + y). Các lệnh này khó demo ở đây, nên tôi khuyến khích bạn tự thử.

Cuối cùng không kém, tôi muốn nhắc một tổ hợp phím hiếm khi dùng nhưng cực kỳ tiện trong môi trường chật hẹp như container. Nếu bạn bao giờ có lệnh trông bị lỗi bởi đầu ra trước, có giải pháp: Nhấn ctrl + x + ctrl + e sẽ mở lệnh trong editor nào được đặt trong biến môi trường EDITOR. Điều này cho phép chỉnh sửa lệnh dài hoặc bị lỗi trong trình soạn thảo văn bản (có khả năng) wrap text. Lưu công việc và thoát, giống như làm việc trên tệp bình thường, sẽ thực thi lệnh khi rời editor.

Mẹo Khác

Bạn có thể thấy việc hiển thị màu trong shell Bash tăng trải nghiệm. Nếu bạn dùng phiên không bật màu, dưới là chuỗi lệnh bạn có thể đặt trong .bash_profile để thêm màu cho phiên. Các lệnh này khá trực tiếp và không cần giải thích sâu:

# enable colors
eval "`dircolors -b`"

# force ls to always use color and type indicators
alias ls='ls -hF --color=auto'

# make the dir command work kinda like in windows (long format)
alias dir='ls --color=auto --format=long'

# make grep highlight results using color
export GREP_OPTIONS='--color=auto'

# Add some colour to LESS/MAN pages
export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;33m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m'
export LESS_TERMCAP_so=$'\E[01;42;30m'
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;36m'

Bên cạnh điều chỉnh các tùy chọn trong Bash, bạn cũng có thể dùng một vài mẹo hay để tiết kiệm thời gian. Ví dụ, để chạy hai lệnh liên tiếp, bất kể trạng thái thoát của mỗi cái, dùng ; tách lệnh, như dưới:

[user@centos /tmp]$ du -hsc * ; df -h

Điều này đơn giản tính dung lượng mỗi tệp trong thư mục hiện tại (và tổng), sau đó truy vấn hệ thống cho dùng ổ đĩa per block device. Các lệnh sẽ chạy bất kể lỗi nào do lệnh du sinh ra.

Điều gì nếu bạn muốn hành động được thực hiện khi hoàn thành thành công lệnh đầu tiên? Bạn có thể dùng viết tắt && chỉ ra bạn muốn chạy lệnh thứ hai chỉ nếu lệnh đầu tiên trả về trạng thái thoát thành công. Ví dụ, giả sử bạn muốn reboot máy chỉ nếu cập nhật thành công:

[root@arch ~]$ pacman -Syu --noconfirm && reboot

Đôi khi chạy lệnh, bạn có thể muốn bắt đầu ra của nó. Hầu hết biết lệnh tee, sao chép đầu ra chuẩn ra cả terminal và tệp. Tuy nhiên, nếu bạn muốn bắt đầu ra phức tạp từ, ví dụ, strace, bạn sẽ cần bắt đầu làm việc với chuyển hướng I/O. Chi tiết chuyển hướng I/O ngoài phạm vi bài viết ngắn, nhưng cho mục đích chúng ta quan tâm STDOUT và STDERR. Cách tốt nhất bắt chính xác thứ bạn thấy là kết hợp hai vào một tệp. Để làm vậy, dùng chuyển hướng 2>&1.

[root@arch ~]$ strace -p 1140 > strace_output.txt 2>&1

Điều này sẽ đặt tất cả đầu ra liên quan vào tệp strace_output.txt để xem sau.

Đôi khi trong lệnh chạy lâu, bạn có thể cần tạm dừng thực thi tác vụ. Bạn có thể dùng phím tắt 'stop' ctrl + z để dừng (nhưng không kill) một job. Job được thêm vào hàng đợi job, nhưng bạn không còn thấy job cho đến khi resume. Job này có thể resume sau bằng lệnh foreground fg.

Thêm vào đó, bạn cũng có thể đơn giản tạm dừng job với ctrl + s. Job và đầu ra ở foreground terminal, và dùng shell không được trả về cho người dùng. Job có thể resume bằng nhấn ctrl + q.

Nếu bạn làm việc trong môi trường đồ họa với nhiều terminal mở, bạn có thể thấy tiện có phím tắt copy/paste đầu ra. Để làm vậy, dùng phím tắt sau:

# Copies highlighted text
ctrl + shift + c

# Pastes text in buffer
ctrl + shift + v

Giả sử trong đầu ra của lệnh đang chạy bạn thấy lệnh khác được thực thi, và bạn muốn thông tin thêm. Có vài cách làm. Nếu lệnh này ở đâu đó trong PATH, bạn có thể chạy lệnh which để tìm ra lệnh đó ở đâu trên ổ đĩa:

[root@arch ~]$ which ls
/usr/bin/ls

Với thông tin này, bạn có thể kiểm tra binary với lệnh file:

[root@arch ~]$ file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d4e02b88e596e4f82c6cc62a5bc4ce5827209a49, stripped

Bạn thấy mọi loại thông tin, nhưng quan trọng nhất cho hầu hết người dùng là phần ELF 64-bit LSB. Điều này về cơ bản nghĩa là nó là binary đã biên dịch sẵn khác với script hoặc loại thực thi khác. Công cụ liên quan bạn có thể dùng để kiểm tra lệnh là công cụ command chính nó. Chỉ chạy command -V sẽ cho thông tin khác:

[root@arch ~]$ command -V ls
ls is aliased to `ls --color=auto`

[root@arch ~]$ command -V bash
bash is /usr/bin/bash

[root@arch ~]$ command -V shopt
shopt is a shell builtin

Cuối cùng chắc chắn không kém, một trong những mẹo yêu thích của tôi, đặc biệt khi làm việc với container hoặc môi trường tôi ít biết/khiêm soát, là lệnh echo. Lệnh này có thể dùng làm mọi thứ từ kiểm tra vòng lặp for chạy chuỗi dự kiến đến cho phép kiểm tra cổng remote có mở không. Cú pháp rất đơn giản kiểm tra cổng mở: echo > /dev///. Ví dụ:

user@ubuntu-1604:~$ echo > /dev/tcp/192.168.99.99/222
-bash: connect: Connection refused
-bash: /dev/tcp/192.168.99.99/222: Connection refused

user@ubuntu-1604:~$ echo > /dev/tcp/192.168.99.99/22

Nếu cổng đóng cho loại kết nối bạn thử, bạn nhận thông báo Connection refused. Nếu gói tin gửi thành công, không có đầu ra.

Tôi hy vọng các mẹo này làm Bash hiệu quả và thú vị hơn. Có nhiều mẹo ẩn trong Bash hơn tôi liệt kê. Những cái yêu thích của bạn là gì?

Phụ Lục 1. Danh sách mẹo và thủ thuật đã bao phủ

# History related
ctrl + r (reverse search)
!! (rerun last command)
!* (reuse arguments from previous command)
!$ (use last argument of last command)
shopt -s histappend (allow multiple terminals to write to the history file)
history | awk 'BEGIN {FS="[ \t]+|\\|"} {print $3}' | sort | uniq -c | sort -nr | head (list the most used history commands)

# File and navigation
cp /home/foo/realllylongname.cpp{,-old}
cd -
rename 's/text_to_find/been_renamed/' *.txt
export CDPATH='/var/log:~' (variable is used with the cd built-in.)

# Colourize bash

# enable colors
eval "`dircolors -b`"
# force ls to always use color and type indicators
alias ls='ls -hF --color=auto'
# make the dir command work kinda like in windows (long format)
alias dir='ls --color=auto --format=long'
# make grep highlight results using color
export GREP_OPTIONS='--color=auto'

export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;33m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m' # end the info box
export LESS_TERMCAP_so=$'\E[01;42;30m' # begin the info box
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;36m'

# Bash shortcuts
shopt -s cdspell (corrects typoos)
ctrl + _ (undo)
ctrl + arrow (move forward a word)
ctrl + a (move cursor to start)
ctrl + e (move cursor to end)
ctrl + k (cuts everything after the cursor)
ctrl + l (clears screen)
ctrl + q (resume command that is in the foreground)
ctrl + s (pause a long running command in the foreground)
ctrl + t (swap two characters)
ctrl + u (cuts everything before the cursor)
ctrl + x + ctrl + e (opens the command string in an editor so that you can edit it before it runs)
ctrl + x + * (expand glob/star)
ctrl + xx (move to the opposite end of the line)
ctrl + y (pastes from the buffer)
ctrl + shift + c/v (copy/paste into terminal)

# Running commands in sequence
&& (run second command if the first is successful)
; (run second command regardless of success of first one)

# Redirecting I/O
2>&1 (redirect stdout and stderr to a file)

# check for open ports
echo > /dev/tcp//
`` (use back ticks to shell out)

# Examine executable
which 
file 
command -V  (tells you whether  is a built-in, binary or alias)