Hiển thị thư mục hiện tại trên tiêu đề terminal bằng FreeBSD sh
Khi mở nhiều terminal cùng lúc, việc nhận biết thư mục làm việc của từng cửa sổ ngay trên thanh tiêu đề sẽ rất tiện lợi.
Ví dụ, nếu thư mục hiện tại là:
/home/hodong/projects/nimfsoft.com
tiêu đề terminal có thể hiển thị:
~/projects/nimfsoft.com
Nếu thư mục hiện tại nằm ngoài thư mục home, đường dẫn đầy đủ sẽ được hiển thị:
/usr/local/src
Bài viết này sử dụng /bin/sh và ~/.shrc trên FreeBSD.
Cơ chế thay đổi tiêu đề terminal
Các trình giả lập terminal tương thích xterm có thể thay đổi tiêu đề bằng chuỗi thoát OSC, viết tắt của Operating System Command.
printf '\033]0;%s\007' 'Tiêu đề terminal mới'
Ý nghĩa của từng phần:
\033 Ký tự ESC
]0; Đặt tên biểu tượng và tiêu đề cửa sổ
%s Chuỗi cần hiển thị
\007 Kết thúc chuỗi OSC bằng BEL
Do đó, có thể dùng trực tiếp thư mục hiện tại:
printf '\033]0;%s\007' "$PWD"
Khả năng hỗ trợ phụ thuộc vào trình giả lập terminal và cấu hình của nó.
Rút gọn thư mục home thành ~
Khi $PWD bằng $HOME, hoặc nằm bên dưới $HOME, tiền tố thư mục home sẽ được
thay bằng ~.
set_terminal_title() {
if [ -n "${HOME-}" ]; then
case "$PWD" in
"$HOME")
_terminal_title_path='~'
;;
"$HOME"/*)
_terminal_title_path="~${PWD#"$HOME"}"
;;
*)
_terminal_title_path=$PWD
;;
esac
else
_terminal_title_path=$PWD
fi
printf '\033]0;%s\007' "$_terminal_title_path"
unset _terminal_title_path
}
${HOME-} được mở rộng thành chuỗi rỗng khi biến HOME chưa được đặt. Điều này
ngăn một giá trị chưa được thiết lập bị xem như tiền tố thư mục home hợp lệ.
Khi thư mục home đã được thiết lập đúng, câu lệnh case phân loại đường dẫn
hiện tại.
case "$PWD" in
"$HOME")
_terminal_title_path='~'
;;
"$HOME"/*)
_terminal_title_path="~${PWD#"$HOME"}"
;;
*)
_terminal_title_path=$PWD
;;
esac
Nếu thư mục hiện tại chính xác là thư mục home, tiêu đề sẽ là ~.
/home/hodong -> ~
Nếu thư mục hiện tại nằm bên dưới thư mục home, phần $HOME sẽ bị loại bỏ và
~ được đặt trước phần đường dẫn còn lại.
/home/hodong/projects/nimfsoft.com
-> ~/projects/nimfsoft.com
${PWD#"$HOME"} xóa tiền tố ngắn nhất khớp với $HOME khỏi đầu $PWD.
Ví dụ, với môi trường sau:
HOME=/home/hodong
PWD=/home/hodong/projects/nimfsoft.com
kết quả là:
/projects/nimfsoft.com
Thêm ~ vào đầu sẽ tạo thành tiêu đề cuối cùng:
~/projects/nimfsoft.com
Việc dùng riêng mẫu "$HOME"/* tránh nhận nhầm
/home/hodong2/projects là thư mục con của /home/hodong.
/home/hodong2/projects
-> /home/hodong2/projects
_terminal_title_path là biến shell tạm thời. Biến được tạo trong hàm shell mặc
định vẫn tồn tại trong shell hiện tại, vì vậy biến này được xóa bằng unset
sau khi sử dụng để giảm khả năng xung đột tên.
Cập nhật tiêu đề sau khi chạy cd
Chỉ đặt tiêu đề một lần khi shell khởi động là chưa đủ. Tiêu đề cũng cần được cập nhật sau mỗi lần thư mục làm việc thay đổi.
Hàm sau gọi lệnh cd tích hợp ban đầu của shell, sau đó chỉ cập nhật tiêu đề khi
việc chuyển thư mục thành công:
cd() {
command cd "$@" || return $?
set_terminal_title
}
Nếu gọi trực tiếp cd "$@" bên trong hàm, chính hàm này sẽ được gọi lại và gây
đệ quy.
cd "$@"
command cd "$@" bỏ qua hàm cùng tên và gọi lệnh cd tích hợp của shell.
command cd "$@"
"$@" giữ nguyên toàn bộ đối số:
cd
cd -
cd /usr/local/src
Nếu việc chuyển thư mục thất bại, trạng thái lỗi được trả về và tiêu đề không thay đổi.
command cd "$@" || return $?
Do đó, khi thử chuyển đến một thư mục không tồn tại, cả thư mục hiện tại và tiêu đề terminal đều được giữ nguyên.
Bản vá .shrc hoàn chỉnh
Áp dụng bản vá sau vào ~/.shrc:
--- a/.shrc 2026-07-15 19:28:53.358286000 +0900
+++ b/.shrc 2026-07-15 19:47:56.461994000 +0900
@@ -46,6 +46,37 @@
bind ^[[5~ ed-move-to-beg
bind ^[[6~ ed-move-to-end
+# Update terminal title to the current working directory.
+set_terminal_title() {
+ if [ -n "${HOME-}" ]; then
+ case "$PWD" in
+ "$HOME")
+ _terminal_title_path='~'
+ ;;
+ "$HOME"/*)
+ _terminal_title_path="~${PWD#"$HOME"}"
+ ;;
+ *)
+ _terminal_title_path=$PWD
+ ;;
+ esac
+ else
+ _terminal_title_path=$PWD
+ fi
+
+ printf '\033]0;%s\007' "$_terminal_title_path"
+ unset _terminal_title_path
+}
+
+# Change directory and update the terminal title.
+cd() {
+ command cd "$@" || return $?
+ set_terminal_title
+}
+
+# Set the initial terminal title.
+set_terminal_title
+
# set prompt: ``username@hostname:directory $ ''
PS1="\u@\h:\w \\$ "
Lần gọi set_terminal_title đầu tiên phản ánh thư mục làm việc tại thời điểm
.shrc được nạp.
set_terminal_title
Sau đó, hàm bao cd sẽ cập nhật tiêu đề sau mỗi lần chuyển thư mục thành
công.
Nạp lại cấu hình:
. ~/.shrc
Khi chuyển vào thư mục dự án:
cd ~/projects/nimfsoft.com
tiêu đề sẽ là:
~/projects/nimfsoft.com
Khi chuyển ra ngoài thư mục home, đường dẫn đầy đủ được hiển thị:
cd /usr/local/src
/usr/local/src
Vì sao không gọi hàm trong PS1
Có thể nghĩ đến cách viết:
PS1='$(set_terminal_title)\u@\h:\w \$ '
Tuy nhiên, trong môi trường FreeBSD sh được sử dụng cho bài viết này, cách đó
không hoạt động như mong đợi. Dấu nhắc hiển thị sai và tiêu đề terminal không
được cập nhật.
Vì vậy, dấu nhắc hiện có được giữ nguyên:
PS1="\u@\h:\w \\$ "
Thay vì phụ thuộc vào cách shell đánh giá lại dấu nhắc, cấu hình cập nhật tiêu
đề ngay sau khi cd thành công.
Lưu ý
Cấu hình này yêu cầu trình giả lập terminal hỗ trợ chuỗi tiêu đề OSC và cho phép ứng dụng thay đổi tiêu đề.
Ví dụ giả định tên thư mục thông thường. Nếu tên thư mục có thể chứa ký tự điều khiển terminal như ESC hoặc BEL, đường dẫn cần được làm sạch trước khi đưa vào chuỗi OSC.
Tiêu đề được cập nhật khi thư mục thay đổi thông qua hàm cd được định nghĩa ở
đây. Lần gọi ban đầu xử lý thư mục khởi đầu của shell.
Tóm tắt
Quy tắc hiển thị cuối cùng:
/home/hodong -> ~
/home/hodong/projects -> ~/projects
/home/hodong/projects/nimfsoft.com -> ~/projects/nimfsoft.com
/home/hodong2/projects -> /home/hodong2/projects
/usr/local/src -> /usr/local/src
Cấu hình nhỏ này giúp phân biệt các cửa sổ terminal dễ dàng hơn khi làm việc đồng thời trên nhiều dự án.