71 điểm bởi GN⁺ 26 ngày trước | 3 bình luận | Chia sẻ qua WhatsApp
  • Tổng hợp các cách tận dụng phím tắt bàn phím và tổ hợp lệnh khác nhau để tăng tốc độ và hiệu quả làm việc trong môi trường shell
  • Có thể chỉnh sửa dòng lệnh bằng các phím tắt cơ bản như CTRL + W, U, K, Y, di chuyển con trỏ với CTRL + A/E, ALT + B/F, và khôi phục terminal bằng reset
  • Trong Bash và Zsh, có thể dùng CTRL + R để tìm kiếm lịch sử lệnh, tái sử dụng lệnh trước đó bằng !!·!$·ESC+., và tự động hóa tác vụ lặp lại với Brace ExpansionGlobstar
  • Các tính năng nâng cao như Process Substitution, tee, bg/disown giúp kiểm soát tiến trình và quản lý log hiệu quả hơn
  • Nếu dùng shell một cách thành thạo như một hộp dụng cụ, bạn có thể nâng cao năng suất và sự tập trung; điều quan trọng là hình thành thói quen từng phím tắt một

Các phím tắt và tính năng cơ bản hoạt động trong gần như mọi shell

  • Tổ hợp CTRL + W, U, K, Ylà các phím tắt chỉnh sửa dòng lệnh giúp xóa hoặc khôi phục nhanh từ hay phần đầu/cuối dòng dựa trên vị trí con trỏ

    • CTRL + W xóa từ phía trước con trỏ
    • CTRL + U cắt từ vị trí con trỏ đến đầu dòng, có thể dán lại bằng CTRL + Y
    • CTRL + K cắt từ vị trí con trỏ đến cuối dòng
    • CTRL + A / E di chuyển đến đầu và cuối dòng, ALT + B / F di chuyển lùi/tiến theo từng từ
    • Được bật mặc định trong phần lớn terminal; trên Mac cần đặt phím Option thành Meta thì mới hoạt động
    • Lệnh reset hoặc stty sane có thể khôi phục terminal khi màn hình bị hỏng do lỡ in ra tệp nhị phân
    • Dù màn hình bị lỗi vẫn có thể nhập, chỉ cần gõ reset rồi nhấn Enter để khôi phục
    • CTRL + C / D lần lượt dùng để ngắt lệnh và gửi tín hiệu EOF (kết thúc tệp)
    • Nhấn CTRL + D ở prompt trống sẽ thoát shell
    • CTRL + L tương đương lệnh clear, giúp dọn màn hình ngay lập tức
    • Đồng thời giữ nguyên lệnh đang nhập và đưa prompt lên đầu màn hình
    • cd - chuyển ngay về thư mục trước đó, còn pushd / popd cho phép qua lại nhiều vị trí bằng ngăn xếp thư mục
    • > file.txt làm trống tệp mà không xóa nó, nên giữ nguyên quyền hạn và chủ sở hữu
    • Biến $_ dùng lại đối số cuối cùng của lệnh trước
    • Ví dụ: mkdir -p /path/newdir && cd "$_"
    • set -e, set -u là các tùy chọn giúp tăng độ an toàn cho script
    • set -e sẽ thoát ngay khi xảy ra lỗi
    • set -u sẽ báo lỗi khi tham chiếu tới biến chưa được định nghĩa
    • Tổ hợp set -euo pipefail cung cấp lớp bảo vệ mạnh hơn

Các phím tắt và tính năng nâng cao trong Bash và Zsh

  • CTRL + R thực hiện tìm kiếm tăng dần ngược trong lịch sử lệnh
    • Khi nhập từ khóa, có thể tìm và tái sử dụng ngay các lệnh đã chạy trước đó
  • !! chạy lại toàn bộ lệnh trước
    • Có thể dùng sudo !! để chạy lại lệnh vừa rồi với quyền quản trị
  • CTRL + X, CTRL + E mở lệnh đang nhập trong trình soạn thảo mặc định (Vim, Nano, v.v.) để chỉnh sửa rồi thực thi
    • Trong Zsh cần cấu hình riêng
    • Lệnh fc là cách truyền thống để mở lệnh trước đó bằng $EDITOR và chỉnh sửa
  • ESC + . hoặc ALT + . chèn đối số cuối cùng của lệnh ngay trước đó vào vị trí con trỏ
    • Khi nhấn lặp lại, có thể lần lượt duyệt qua đối số của các lệnh cũ hơn
    • !$ thực hiện cùng chức năng đó theo cách không tương tác
  • Brace Expansionlà tính năng mở rộng dấu ngoặc nhọn giúp giảm nhập lặp

    • cp pf.conf{,.bak}cp pf.conf pf.conf.bak
    • mv filename.{txt,md}mv filename.txt filename.md
    • mkdir -p project/{src,tests,docs} để tạo nhiều thư mục cùng lúc
    • Process Substitution <(command) xử lý đầu ra của lệnh như một tệp
    • Ví dụ: diff <(sort file1.txt) <(sort file2.txt) để so sánh mà không cần tệp tạm
    • Globstar(**) tìm tệp đệ quy xuống cả các thư mục con
    • Trong Bash, bật bằng shopt -s globstar; trong Zsh, tính năng này được bật sẵn
    • Ví dụ: ls **/*.js để tìm các tệp JS trong mọi thư mục con
  • Tổ hợp CTRL + Z**,** bg**,** disowngiúpchạy nền tiến trình và tách khỏi shell

    • CTRL + Z để tạm dừng → bg để chạy nền → disown để tách khỏi shell
    • Nhờ đó tiến trình vẫn tiếp tục chạy ngay cả sau khi đóng SSH
    • command |& tee file.log sẽ chuyển đồng thời đầu ra chuẩn (stdout)lỗi chuẩn (stderr) qua pipe
    • |& là dạng rút gọn của 2>&1 |
    • Dùng tee để vừa in ra màn hình vừa lưu log

Lời khuyên cốt lõi khi sử dụng shell

  • Shell là một hộp dụng cụ, và nếu dùng thành thạo có thể tăng năng suất rất nhiều
  • Thay vì cố học tất cả phím tắt cùng lúc, cách hiệu quả hơn là biến chúng thành thói quen từng cái một
  • Có thể đơn giản hóa luồng làm việc bằng cách giảm nhập lặp và thao tác gõ không cần thiết
  • Khi đã quen, terminal sẽ không còn là vật cản mà trở thành không gian làm việc của riêng bạn

3 bình luận

 

Hầu hết đều là key binding của emacs, nhưng gần như chẳng nhắc gì đến Emacs; nhìn chúng được chia sẻ nhiều như mấy mẹo vặt dạo này lại thấy có cảm giác đổi thay của thời thế. Thư viện readline thực ra là nơi triển khai toàn bộ key binding kiểu Emacs. Có cảm giác đã gần 20 năm kể từ khi mọi thứ hầu như chuyển hết sang IDE, nhưng từ góc nhìn của một hóa thạch vẫn còn dùng thứ như gdbtui, tôi lại nghĩ rằng cái gì tốt thì quả nhiên không chết.

 
Ý kiến trên Hacker News
  • Khoảnh khắc mình cảm thấy cuộc đời thay đổi là khi remap phím mũi tên lên
    Giờ mình không phải duyệt qua mọi lệnh nữa, mà chỉ tìm các lệnh bắt đầu bằng phần đã gõ sẵn
    Ví dụ, gõ tar - rồi nhấn mũi tên lên thì các tùy chọn tar đã dùng trước đó hiện ra ngay
    Trong zsh có thể cấu hình như sau

    bindkey "^[OA" up-line-or-beginning-search # Up  
    bindkey "^[OB" down-line-or-beginning-search # Down
    
    • Một khi đã bắt đầu dùng CTRL+r thì bạn sẽ không bao giờ dùng lại mũi tên lên nữa
    • Mình nghĩ đây đúng là một game changer. Nếu muốn làm tương tự trong bash thì thêm vào .inputrc như sau
      "\e[A":history-search-backward  
      "\e[B":history-search-forward
      
    • Trong fish shell thì đây là hành vi mặc định. Nó duyệt lệnh bằng mũi tên lên/xuống dựa trên từ đã gõ, và còn có thể dùng alt+lên/xuống để chỉ duyệt các đối số
    • Có thể triển khai y hệt trong .inputrc
    • Mình giữ nguyên mũi tên lên/xuống, thay vào đó cấu hình ctrl+p và ctrl+n theo cách này
  • Bật vim-mode trong terminal khiến mọi thứ thoải mái hơn hẳn
    Lỡ sai ở chỗ cách ba từ? 3bcw là xong
    Xóa cả dòng thì cc, chỉnh sửa phức tạp thì bấm v để mở thẳng trong (neo)vim
    Nếu bạn đã dùng (neo)vim rồi thì quá tuyệt vì không cần học thêm phím tắt mới

    • Mình nhớ đến một đoạn trong manual Emacs ngày xưa. Với những lệnh phức tạp, mình muốn khuyên dùng thiết lập chuột hơn
    • Mình đã dùng (n)vim hơn 20 năm nhưng vẫn ghét vi-mode của shell. Khi cần lệnh phức tạp thì dùng ctrl-x+e để mở bằng neovim là một thỏa hiệp hợp lý
    • Mình cũng dùng vim thường xuyên nhưng không dùng trong shell. Thay vào đó, mình dùng phím tắt kiểu Emacs (ctrl-a, ctrl-e, v.v.) trên toàn hệ thống macOS. Mình ước vim hỗ trợ binding kiểu emacs ngay trong insert mode
    • Đây là lần đầu mình biết tên tính năng là “readline vi-mode”. Phải tìm hiểu thêm mới được
    • Mình không hiểu vì sao emacs mode lại là mặc định. Mỗi khi mở shell mới, việc đầu tiên mình làm là gõ set -o vi
  • Mình thấy ấn tượng với chuyện có người đặt một script tên \# vào PATH để có thể comment out một phần của pipeline

    #!/bin/sh
    cat
    
    • Mình cũng dùng một mẹo tương tự. Trong file ~/bin/noglob
      #!/bin/sh
      $*
      
      làm như vậy để tránh xung đột noglob khi gọi script zsh từ bash
    • Mình dùng comment bằng dấu ngoặc. Cách này tốt cho việc viết tài liệu script nhưng khá bất tiện trong interactive shell. Dù vậy đây vẫn là một cách comment rất sáng tạo nên khá thú vị
    • Mình tò mò không biết nó có ưu điểm gì so với mycmd1 #| mycmd2
    • Cái này thực sự hữu ích, mình định thêm nó vào $PATH của mình
  • CTRL+W thường xóa đến khoảng trắng trước đó, nên nó sẽ xóa cả /var/log/nginx/
    Alt+Backspace thì xóa đến trước ký tự không phải chữ cái
    Chỉ cần lưu ý là nó có thể trùng với phím tắt đóng tab trình duyệt

    • Từ Firefox v147 đã có tính năng gán lại phím tắt. Liên kết liên quan
    • Trên macOS thì phím tắt GUI và phím tắt terminal được tách riêng nên không có vấn đề này. ⌘C, ⌘W v.v. đều hoạt động nhất quán ngay cả trong terminal
    • Trong môi trường của mình (fish + Alacritty) thì lại ngược lại. Dù sao cũng may là có thể khôi phục tab vừa đóng bằng Ctrl-Shift-T
    • Nếu bỏ / khỏi cấu hình $WORDCHARS thì sẽ có được hành vi mong muốn (liên kết tham khảo)
    • Có thể khôi phục tab ngay bằng Ctrl-Shift-T
  • Mình khuyên nên nâng cấp tìm kiếm lịch sử bằng fzf shell integration
    Video demo / Tài liệu chính thức

  • Mình có một mẹo hay dùng
    Khi đang gõ một lệnh dài mà cần làm việc khác trước, mình không Ctrl-C để hủy mà sẽ comment nó lại rồi chạy để nó vẫn nằm trong history

    $ long_command  
    $ #long_command  
    $ stuff_1  
    $ stuff_2  
    $ #long_command  
    $ long_command
    
    • Trong zsh có thể làm bằng binding "push-line-or-edit", còn trong bash thì có thể mô phỏng gần giống bằng C-u rồi C-y
    • Cách dễ hơn là dùng ctrl-u để lưu và xóa dòng hiện tại, rồi ctrl-y để dán lại sau. Trong zsh có thể tự động hóa quy trình này bằng alt-q
    • Trong bash, alt-shift-3 có thể thêm # vào đầu lệnh hiện tại rồi chuyển sang dòng mới
  • Mình không thích kiểu tiêu đề mang hơi hướng LLM, nhưng vài mẹo trong đó khá hữu ích nên mình sẽ thử
    Khá tiếc là brace expansion không phối hợp tốt với tab completion. Sẽ hay hơn nếu có thêm tính năng như nhân bản token cuối hoặc xóa phần mở rộng file

    • Văn phong của bài viết cũng có cảm giác như do LLM tạo ra. Ví dụ câu kiểu “The shell is a toolbox, not an obstacle course.”
    • PowerShell cũng có readline mode, nên khi dùng song song với WSL thì đỡ bị khựng khi chuyển qua lại
    • readline hoạt động như một phần của bash, nên mình nghĩ gọi những mẹo này là tính năng của shell cũng không sai
  • Mình dùng một hàm trong cấu hình zsh để loại một số lệnh khỏi history
    Ví dụ để những lệnh nguy hiểm như --force không bị lưu vào history

    function zshaddhistory() {
      emulate -L zsh
      if ! [[ "$1" =~ "(^ |--force|whatever)" ]] ; then
        print -sr -- "${1%%$'\n'}"
        fc -p
      else
        return 1
      fi
    }
    
    • Mình cũng làm kiểu tương tự, tạo sẵn alias thêm dấu cách ở đầu cho những lệnh không muốn ghi lại
      unhist () { alias $1=" $1"; }
      unhist unhist
      unhist fzf
      unhist rghist
      
  • Mình dùng một snippet trong zsh để tự động mở rộng global alias
    Ví dụ đặt alias -G G='rg -s' thì khi gõ command | G, nó sẽ tự động mở rộng thành command | rg -s
    Nó tự mở rộng khi nhấn space, còn nếu gõ \alias thì sẽ bỏ qua việc mở rộng

  • Mình có một hàm “deep cd” tự viết trong .bashrc
    Nó tự chuyển vào thư mục đầu tiên có đường dẫn chứa chuỗi được truyền làm đối số

    dcd() {
      [ -z "$1" ] && return
      local dir
      dir=$(find . -type d -path "*$1*" -print -quit 2>/dev/null)
      [ -n "$dir" ] && cd "$dir"
    }
    

    Ban đầu mình nghĩ nó sẽ chậm, nhưng hóa ra dùng thường xuyên hơn mình tưởng

    • Có thể xem thêm các công cụ smart cd như jc của autojump, z, hoặc fzf
 

Phía trên, vụ remap phím mũi tên lên thì người dùng Emacs cứ dùng c-r là xong. Đỉnh cao của tinh chỉnh là để nguyên bản.