Tìm kiếm Blog này

Thứ Bảy, 31 tháng 12, 2016

Hướng dẫn cài driver đồ họa gốc của NVIDIA trên fedora 23/24/25


Đầu tiên là source: https://www.if-not-true-then-false.com/2015/fedora-nvidia-guide/
lấy từ if-not-true-then-false, rất đắt lực dành cho người dùng fedora.
bản fedora em đang xài là 25, nên trong bài dịch bản khác thì không biết nhưng bản 25 cùng con card giga gt 420 thì thành công.


Bắt đầu:

Bài viết sẽ hướng dẫn cài đặt driver gốc tải trực tiếp từ nvidia (nVidia proprietary drivers) và cách tắt driver Nouveau (driver mặc định nguồn mở, có sẵn trên fedora, tất nhiên nó sẽ ổn định hơn nhưng khai thác hiệu năng thì không bằng thằng gốc chính chủ). Bài viết dùng cho các loại card GeForce 6/7/8/9/200/300/400/500/600/700/800/900/10.
  • các con card GeForce 400/500/600/700/800/900/10 sẽ dùng driver bản 367.xx hoặc 370.xx hoặc 375.xx.
  • GeForce 8/9/200/300 dùng driver bản 340.xx
  • GeForce 6/7 dùng driver bản 304.xx
(Xin cảm ơn vì các bác đã chịu khó đọc 3 dòng trên vì khi vào trang chủ của Nvidia tải driver thì có phần chọn tên card và OS thì nó cũng ra phiên bản phù hợp thôi :)) )
Như nói ở trên, bản hướng dẫn này sẽ dùng driver gốc từ nvidia và dkms để cập nhật kernel (dkms là cái gì thì mượn tạm cái wiki của archlinux link, dkms sẽ được cài từ repo), không phải driver lấy từ các repo (cũng như từ RPMFusion) khác như trong bài hướng dẫn cũ này (link). Tất nhiên cài từ các repo này sẽ dễ dàng hơn nhưng có thể sẽ phát sinh lỗi nghiêm trọng, vì thế chủ nhân bài viết đã hướng dẫn cách cài thủ công này. Người viết bài đã kiểm tra trên các phiên bản 304.xx (patched), 340.xx, 367.xx, 370.xx and 375.xx drivers/cards.

Cùng với DKMS, công việc cập nhật kernel và biên dịch driver nvidia sẽ được tự động hóa. nếu muốn cập nhật driver nvidia hãy download bản mới nhất từ trang chủ nvidia hoặc đơn giản chạy lệnh "nvidia-installer --update" (nó sẽ hoạt động với bản driver mới nhất), nếu cập nhật theo cách này với phiên bản 304.xx or 340.xx, bạn sẽ được bản mới nhất. Vậy nên hãy cập nhật tất cả để xài cho khỏe.

Khi sử dụng hướng dẫn này hãy:
Nếu thất cmn bại: Nói cho người viết bài nếu bác gặp vấn đề nào đó lúc đang cài đặt (reply ở link gốc nhé link, em chỉ là thằng dịch thôi)
Nếu thành công: hãy post hình cái terminal của các bác sau khi chạy mấy cái lệnh này:
nvidia-installer -v |grep version
uname -a
lspci |grep -i VGA
Một thao tác thể hiện sự lịch sự khi dùng tut miễn phí, để người sau có thêm thông tin về tut này.
Trước khi cài đặt nên hãy sao lưu những dữ liệu quan trọng. Đây là chuyện tất nhiên để ứng biến với mọi tình huống gây ra do sự kết hợp giữa card màn hình, các thành phần OS và màn hình, sự kết hợp này sẽ tạo những kết quả "ngoài mong đợi". (thêm mắm dăm muối cho dài, cơ bản là sao lưu dữ liệu cho nó an toàn, và để loại trừ trách nhiệm cho người viết lẫn người dịch :)) ).

1, CHUẨN BỊ CÀI ĐẶT:

1.1, Kiểm tra card có được hỗ trợ hay không ?

Mấy cái này đi nói với mấy bác voz chả khác nào múa rìu qua mắt thợ, thấy lây nhây các bác bỏ qua bước này, tải được đúng cái driver trên trang chủ Nvidia là hết bước này. Em vẫn sẽ dịch cho những người chưa biết.
Lấy cái model con card của các đã:
lspci |grep -E "VGA|3D"
## Example outputs ##
01:00.0 VGA compatible controller: NVIDIA Corporation GF119 [GeForce GT 610] (rev a1)
trong cái output ví dụ thì GT 610 là model con card của các bác. dò xem nó có trong này không ? List of Supported NVIDIA GPU Products. Nếu xuất hiện trong list trước phần 340.xx, thì sử dụng driver bản 367.xx hoặc 370.xx hoặc 375.xx. Nếu nó ở giữ phần 340.xx và 304.xx thì sử dụng driver 340.xx. Còn sau 340.xx thì cứ dùng luôn bản 340.xx.
(Nếu đã đọc xong phần mỏi mắt trên kia thì em chỉ các bác cách này hay hơn, đó là dùng chức năng tìm kiếm thủ công trên trang chủ của nvidia, rất dễ dàng. link :http://www.nvidia.com/download/index.aspx. Cứ chọn thông tin card và OS (nếu không thấy linux thì bấm vào show all OS) bấm search thôi, ra rồi thì bấm vào "suported product" xem coi card của mình có trong đó không ?. Có thì tải về, khỏi lăn tăn. ngày 12/12/2016 thử card GT 420 thì ra bản 375.20 đấy các bác.
Còn nếu cái output của lệnh trên kia ra trên 2 dòng (tức là có trên 2 VGA controller) thì hãy đọc tiếp, đó chính là ví dụ output ở mục kế tiếp. Đây là trường hợp cái máy của các bác có tính năng optimus, (optimus là cái gì thì link, bài viết của tinhte) nói tóm tắt là khi các bác lắp 1 con card nvidia vào thì hoặc là dùng chip intel đi theo CPU hoặc dùng card các bác vừa lắp, quyết định bằng bios nhé. Nhưng nếu có tính năng optimus thì máy sẽ dùng cả 2, nếu chỉ gõ text văn bản thì OS dùng chip của Intel, còn chơi game coi phim bậy thì nó sẽ tự lấy card rời ra, không phải cần reset thủ công. Thông thường, cho đơn giản, vào bios tắt tính năng này và chọn xuất card rời luôn cho đỡ lằng nhằng, ở dưới sẽ nói thêm về nó.

1.2 NVIDIA Optimus Technology:

Đây là 2 kết quả thí dụ card có Optimus khi dùng lệnh " lspci |grep -E “VGA|3D”":
00:02.0 VGA compatible controller: Intel Corporation 2n Generation Core Processor Family Integrated Graphics Controller (rev 09)
01:00.0 VGA compatible controller: nVidia Corporation GF106 [GeForce GT 555M SDDR3] (rev a1)

00:02.0 VGA compatible controller: Intel Corporation 4th Gen Core Processor Integrated Graphics Controller (rev 06)
01:00.0 3D controller: NVIDIA Corporation GK107M [GeForce GT 750M] (rev a1)
Nếu không tắt được cái tính năng này thì giải tán hội nghị, bản hướng dẫn này chưa test trên máy khi sử dụng tính năng này. Đọc thêm dự án Bumblebee (link http://bumblebee-project.org/)

2, Tiến hành cài đặt.

2.1, tải package cài đặt:

Sau khi đã xong cái phần 1 chuẩn bị cài đặt,trên kia thì chỉ cần lên trang chủ nvidia và tải về bản driver phù hợp với mình thôi. link :http://www.nvidia.com/Download/index.aspx?lang=en-us. Khi tải về nó sẽ có dạng NVIDIA-Linux-xxxx.run, tí nữa sẽ bàn sau, cứ tải về đã. Bác nào xài phiên bản mới nhất thì chả còn gì để nói, bác nào xài đồ cũ thì đọc tiếp 1 tí về cái kết quả test của chủ bài viết.
Fedora 24/25 cùng với kernel 4.8 sẽ cần 1 bản vá của bản cài đặt driver nvidia 304xx (link https://www.if-not-true-then-false.com/2015/fedora-nvidia-guide/4/#download-nvidia-patched-installer)

Những phiên bản đã test:
Fedora 25 Fedora 24 Fedora 23 Fedora 22/21
375.26 (December 14, 2016) 375.26 (December 14, 2016) 375.26 (December 14, 2016) 375.26 (December 14, 2016)
340.101 (December 14, 2016) 340.101 (December 14, 2016) 340.101 (December 14, 2016) 340.101 (December 14, 2016)
304.134 (December 14, 2016) 304.134 (December 14, 2016) 304.134 (December 14, 2016) 304.134 (December 14, 2016)

Chú ý: phiên bản 304.134 trên Fedora 25/24/23 - cài đặt và khởi động được, Nhưng nó sẽ không hoạt động trên Gnome 3.22/3.20/3.18. DEs/WMs Khác hoạt động bình thường. Kernel 4.8/4.7/4.6 cần một phiên bản vá, link: https://www.if-not-true-then-false.com/2015/fedora-nvidia-guide/4/#download-nvidia-patched-installer
lây nhây vậy đủ rồi, tóm lại: tải cái driver dạng .run về rồi phải không ?
Tiến trình cài đặt hãy nhớ 2 điều kiện:
  1. Cài đặt chỉ thực hiện trong môi trường dòng lệnh với dịch vụ đồ họa bị tắt(trường hợp fedora thì sẽ chạy trên runlevel 3, runlevel là cái gì thì đọc link)
  2. Disable driver open source có sẵn trong fedora.
Tí nữa sẽ hướng dẫn đầy đủ 2 điều kiện này.

2.2, cấp quyền thực thi cho driver

cấp quyền thực thi cho driver các bác vừa tải:
chmod +x /path/to/NVIDIA-Linux-*.run

2.3, chuyển qua sử dụng quyền root:

su -
## hoac ##
sudo -i

2.4, cập nhật hệ thống:

Nếu xuất hiện dòng exclude=xorg-x11* trong file /etc/dnf/dnf.conf thì hãy xóa bỏ nó.
## Fedora 25/24/23/22 ##
dnf update
## Fedora 21 ##
yum update
Sau khi cập nhật xong hãy khởi động lại và sử dụng kernel mới nhất được cập nhật.
reboot

2.5 Cài đặt các gói phụ thuộc.

## Fedora 25/24/23/22 ##
dnf install kernel-devel kernel-headers gcc dkms acpid
## Fedora 21 ##
yum install kernel-devel kernel-headers gcc dkms acpid

2.6 Tắt driver nouveau:

2.6.1 Tạo ra file /etc/modprobe.d/blacklist.conf

thêm dòng blacklist nouveau vào file vừa tạo:
echo "blacklist nouveau" > /etc/modprobe.d/blacklist.conf

2.6.2 Chỉnh sửa /etc/sysconfig/grub

Thêm chuỗi ‘rd.driver.blacklist=nouveau’ vào cuối dòng ‘GRUB_CMDLINE_LINUX=”…”‘
# Example row ##
GRUB_CMDLINE_LINUX="rd.lvm.lv=fedora/swap rd.lvm.lv=fedora/root rhgb quiet rd.driver.blacklist=nouveau"

2.6.3 Cập nhật cấu hình grub2 vừa chỉnh sửa

## BIOS ##
grub2-mkconfig -o /boot/grub2/grub.cfg
## UEFI ##
grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg

2.6.4 Gỡ bỏ driver xorg-x11-drv-nouveua

## Fedora 25/24/23/22 ##
dnf remove xorg-x11-drv-nouveau
## Fedora 21 ##
yum remove xorg-x11-drv-nouveau
Nếu trong file /etc/dnf/dnf.conf có dòng này, thì gỡ bỏ nó:
exclude=xorg-x11*
(Khúc này không hiểu cho lắm, theo như em biết cái dòng là để cho dnf nó bỏ qua không đụng tới mấy cái gói xorg..., tại sao lại gỡ bỏ khi không dùng tới, máy em để lại dòng này và nó vẫn chạy tốt đấy các bác)

2.6.5 tại file initramfs

initramfs là cái gì thì link, đọc thêm cho biết, không ảnh hưởng gì.
# luu tru initramfs có nouveau ##
mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r)-nouveau.img
## Tao file initramfs moi ##
dracut /boot/initramfs-$(uname -r).img $(uname -r)

2.7 Khỏi động vào runlevel 3

runlevel là cái gì thì link. Chỉ cần biết mức 3 sẽ không có giao diện đồ họa vì thế không thể vừa đọc tut vừa làm như nãy giờ, sau khi chạy 2 lệnh dưới sẽ vào runlevel 3, hãy đảm bảo có thể đọc tiếp hướng dẫn này. Khuyến cáo in ra giấy hoặc sử dụng các ứng dụng duyệt web trên putty như lynx/links/w3m và cách tốt nhất là lưu mấy dòng vắng tắt sau đây ra 1 file text.
nói ngắn gọn để copy vào text thôi, chi tiết sẽ ở phía dưới.
Công việc chỉ tóm gọn: sử dụng quyền root, chạy file cài đặt, thông báo complete thì chuyển về giao diện đồ họa (runlevel 5), reboot và tận hưởng.
sudo -i
#dung lenh cd di toi vi tri dat file NVIDIA-Linux-*.run va chay no
./NVIDIA-Linux-*.run
#xong thi chuyen qua giao dien do hoa
systemctl set-default graphical.target
reboot
các bác copy nó vào 1 file, khi qua runlevel 3 thì dùng 1 putty để cài, 1 putty dùng lệnh cat /path/file để xem. còn chuyển đổi qua lại giữa putty là các tổ hợp ctrl + alt + F1..F6 gì đấy.
Phần 2.8 sẽ ghi chi tiết có kèm hình ảnh, nên đọc trước rồi hãy chuyển qua runlevel 3, còn làm biếng thì thôi chuyển luôn.
systemctl set-default multi-user.target
reboot

2.8 Cài đặt

2.8.1 log tài khoản root.

su -
## OR ##
sudo -i

2.8.2 chạy file cài đặt tải về từ Nvidia

Hồi nãy tải về ở đâu thì dùng lệnh cd đi vào thư mục chứ file cài đặt, chạy lệnh cài:
./NVIDIA-Linux-*.run

2.8.2 Chấp nhận cái điều khoản của nvidia

Chọn accept, tất nhiên.




2.4.8 dùng DKMS đưa module vào source kernel



2.4.9 cài thư viện tương thích 32 bit



2.8.6 Bắt đầu tiến trình cài đặt


Chú ý: nếu có lỗi dưới này thì bấm ignore hoặc continues để tiếp tục, tức là bỏ qua cái lỗi đó:

Cannot create symlink /usr/lib/libGL.so.1 (File exists)
Cannot create symlink /usr/libGL.so (File exists)
Cannot create symlink /usr/libGLX-nvidia.so.0 (File exists)
Lỗi này có thể được fix trong phiên bản kế tiếp. Đọc thêm tại đây: link

2.8.7 Sao lưu Xorg



2.8.8 Cài đặt xong !



Đúc kết là toàn yes yes yes lâu lâu ignore hoặc continues chứ chả có gì bàn cãi :))

2.9 Nếu đã hoàn thành thì trở về giao diện họa (runlevel 5)

systemctl set-default graphical.target
reboot

2.10 cài VDPAU/VAAPI

Cái này sẽ làm tăng tốc độ xử lý video cho các bác (chú ý: cần phải Geforce 8 trở lên)
## Fedora 25/24/23/22 ##
dnf install vdpauinfo libva-vdpau-driver libva-utils
## Fedora 21 ##
yum install vdpauinfo libva-vdpau-driver libva-utils
Trong link source sẽ có 1 số hình ảnh sau khi cài đặt thành công một số loại card, các bác có thể đóng góp bằng cách
nvidia-installer -v |grep version
uname -a
lspci |grep -i VGA
và chạy cái lệnh này:
nvidia-settings
chụp hình lại và cmt trong source.
Hết bài dịch.


Chủ Nhật, 16 tháng 10, 2016

[shell] khai triển mở rộng biến (parameter expansion)

source: http://wiki.bash-hackers.org/syntax/pe

Công dụng:

Khai triển biến như $var nhưng mở rộng thêm các khả năng biến đổi kết quả sau khi khai triển (thay thế, xóa ký tự, thêm ký tự,....).
Dùng cho khai triển mảng (array).

Cách dùng:

1,CÁCH CƠ BẢN:

${parameter}
Cũng tương tự như khai triển thông thường $parameter để lấy giá trị của parameter nhưng có thể thêm ký tự đằng sau dấu }. Lệnh sẽ chỉ khai triển paramter trong dấu {} mà không bị ảnh hưởng bởi ký tự bên ngoài.
VD:
echo "The plural of $WORD is most likely $WORDs"
# bash sẽ hiểu WORDs là 1 biến khác
echo "The plural of $WORD is most likely ${WORD}s"
#khai triển WORD trước rồi mới thêm s sau kết quả
Ngoài ra, còn có thể khai triển các tham số vị trí trong câu lệnh nếu có hơn 10 vị trí mà không cần dùng lệnh shift để lùi vị trí. ( tham số vị trí chỉ có $1...$9 thôi, source)
VD:
set mot hai ba bon nam sau bay tam chin muoi "muoi mot" "muoi hai"
echo ${12}Đối với mảng (array):
$array{[2]}
$array{[@]}
Trong một mảng vị trí bắt đầu là 0, ở một vị trí có thể là phần tử rỗng. sử dụng * và @ để lấy giá trị toàn bộ các phần tử.Đọc thêm
VD:
array=(khong mot hai ba bon nam sau bay tam) #tạo mảng.
echo ${array[2]} #lấy giá trị tham số vị trí 2 của mảng, KQ là "hai"
echo ${array[*]} # lấy giá trị toàn bộ phần tử của mảng.
Bonus: Sự khác biệt giữa * và @, cũng giống khi sử dụng tham số vị trí (đây), nếu không có dấu ngoặc kép ("") thì không khác gì nhau, nhưng khi có ngoặc kép thì khi sử dụng * các phần tử được ngăn cách bởi biến IFS, còn @ sẽ là các dấu ngoặc kép và khoảng trắng, các phần sau này sự khác biệt này vẫn có hiệu lực.
VD:
echo "${array[@]}"
#kết quả sẽ tương đương
"${array[0]}" "${array[1]}" "${array[2]}" "${array[3]}" "${array[4]}" "${array[5]}" "${array[6]}" "${array[7]}" "${array[8]}"
OLD_IFS="$IFS" #backup biến IFS, 1 thói quen tốt.
IFS=:
echo ${array[*]}
#kết quả tương đương "${array[0]}:${array[1]}:${array[2]}:${array[3]}:${array[4]}:${array[5]}:${array[6]}:${array[7]}:${array[8]}"
Cái số trong ngoặc vuông (VD số 2 trong array[2]) là số thứ tự các phần tử mảng, do nếu phần tử bị unset thì số này sẽ bị mất và vậy không có thứ tự nữa, nên sau này giữ nguyên tiếng anh, gọi luôn là số index cho nó đỡ phiền.

2,KHAI TRIỂN GIÁN TIẾP 1 GIÁ TRỊ (INDIRECTION) :

${!parameter} 
Dùng để khai triển 1 biến là giá trị của 1 biến khác, chắc chắn không thể nào dùng cú pháp như này được $$a
VD:
a=b
b=c
echo ${!a} # kết quả sẽ là "c"
set mot hai ba bon
echo ${!#} # lấy giá trị của tham số vị trí cuối cùng, ở đây là "bon" ($#: số lượng tham số vị trí)
Nếu phối hợp với các cú pháp mở rộng khác thì bash sẽ khai triển gián tiếp trước rồi mới tới cái khác.(khúc này hơi lủng củng vì bí, mọi người vào source đọc cho chắc ăn ("--) ), để nhìn thấy rõ, hãy mở terminal và thử ví dụ này.
VD:
a=h
A=d
h=c
echo ${!a^}
# Nếu chỉ ${a^} kết quả sẽ là H ( ^ sẽ in HOA ký tự đầu tiên giá trị lấy được, đọc thêm phía dưới), kết quả ${!a^} sẽ là C. 
Một số trường hợp phép khai triển gián tiếp này không hoạt động:
${!var@}
${!var*}
${!var[@]}
${!var[*]}
${#var}
vì tiền tố ! sẽ bị xung đột với các mẫu "name-reference" khi dùng trên các phiên bản tương tự với ksh. (đoạn này chả biết dịch sao nhưng không chơi với ksh nên không quan tâm (--") )
Ngoài ra, còn có thể dùng lệnh eval trong trường hợp khai triển gián tiếp. VD:
a=b
b=c
eval echo \$$a # Kết quả tương đương echo ${!a}

Đối với mảng

Cú pháp gián tiếp này có thể sử dụng cho mảng trên Bash 3 series (chính xác phiên bản nào thì không rõ), nhưng lại không có bản hướng dẫn chính thức nào. Muốn biết thì đọc thêm trong link.
(Cái ví dụ trong link khó hiểu quá bác nào hiểu được thì reply, giảng em luôn, xong rồi em bổ sung trong này (--") )
Còn thực nghiệm thì nó như thế này (test trên phiên bản bash version 4.3.11(1)-release, thử nghiệm cá nhân không có trong link source):
  • Đối với từng phần tử thì như biến bình thường phía trên.
  • Đối với giá trị toàn phần như array[@] và array[*] nó sẽ hiện ra số thứ tự từng phần tử có trong mảng, ứng bao nhiêu phần tử thì có bấy nhiêu số thứ tự, phần tử rỗng vẫn hiện ra số thứ tự nhưng nếu phần tử bị unset sẽ mất số thứ tự.
VD:
array=(zero one two three four five) #tạo mảng
echo ${!array[@]}
#kết quả 0 1 2 3 4 5
array[3]= #đặt phần tử thứ 3 là phần tử rỗng.
echo ${!array[@]}
#kết quả vẫn là 0 1 2 3 4 5.
unset array[3] #unset phần tử thứ 3.
echo ${!array[@]}
#kết quả 0 1 2 4 5 (mất số 3).
Chưa nghĩ ra công dụng của nó là gì :)).

3, CHỈNH SỬA KÝ TỰ HOA/THƯỜNG (CASE MODIFICATION)

${PARAMETER^}
${PARAMETER^^}
${PARAMETER,}
${PARAMETER,,}
${PARAMETER~}
${PARAMETER~~}
Chuyển đổi ký tự HOA/thường của giá trị sau khi khai triển biến theo ý muốn. Do linux có phân biệt ký tự HOA và thường nên cú pháp này khá hữu dụng trong việc đổi tên file hàng loạt, ... Cách nhớ:
  • Dấu ^ biến đổi ký tự trở thành ký tự HOA.
  • Dấu , biến đổi ký tự trở thành ký tự thường.
  • Dấu ~ thay đổi trạng thái hiện tại của ký tự, nếu là HOA ➞ thường và nếu là thường ➞ HOA.
  • Nếu chỉ 1 dấu thì biến đổi 1 ký tự đầu tiên của giá trị, hai dấu thì biến đổi toàn bộ.
VD:
var1=saMsung
var2=SamSung
echo ${var1^} #kết quả SaMsung
echo ${var2,} #kết quả samSung
echo ${var1~~} #kết quả SAmSUNG
Cái này cũng không quan trọng lắm nhưng vẫn phải ghi ra để tỏ lòng biết ơn. cái cú pháp ~ và ~~kia là không có trong tài liệu chính thức nào cả, là người dùng tự tìm ra. Chân thành cảm ơn 2 thành viên có nick name Bushmills và geirha trên kênh IRC freenode đã tìm ra cái này.
Một VD đơn giản về chuyển tất cả tên file thành ký tự thường.
for file in *.txt; do
mv "$file" "${file,,}"
done

Đối với mảng

Cú pháp chỉnh sửa này hoàn toàn có thể dùng được cho mảng, dù là từng phần tử hay toàn bộ (@ và *).
Đối với từng phần tử: giống với biến thông thường.
Đối với toàn mảng (sử dụng @ và *): nó sẽ tác dụng lên từng giá trị phần tử và hiển thị toàn bộ các giá trị đó lên. VD:
hovaten=(bui thi hoai anh)
echo ${hovaten[2]^} #kết quả Hoai
echo ${hovaten[@]^} #kết quả Bui Thi Hoai Anh

4, HIỂN THỊ CÁC BIẾN CÓ CÙNG TIỀN TỐ

${!PREFIX*}
${!PREFIX@}
cú pháp này sẽ hiển thị các tên biến bắt đầu bởi tiền tố prefix (tên biến thôi và không hiển thị giá trị). Sự khác nhau @ và * cũng giống như trên kia.
VD:
echo ${!BASH*}
#kết quả BASH BASH_ARGC BASH_ARGV BASH_COMMAND BASH_LINENO BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION
Cú pháp này cũng hiển thị luôn cả tên mảng.

5, GỠ BỎ MỘT PHẦN CHUỖI KÝ TỰ CỦA GIÁ TRỊ BIẾN

${PARAMETER#PATTERN}
${PARAMETER##PATTERN}
${PARAMETER%PATTERN}
${PARAMETER%%PATTERN}
Gỡ bỏ một phần chuỗi của giá trị (bỏ phần PATTERN trong giá trị), bắt đầu từ đầu hoặc cuối chuỗi, vậy nên PATTERN phải bắt đầu từ đầu hoặc cuối chuỗi mới có tác dụng. Vì thế cách tạo PATTERN dễ dàng nhất là chọn vài ký tự làm mốc và dùng ký tự * (asterisk) để đại diện cho các ký tự còn lại bắt đầu từ đầu hoặc cuối chuỗi (đọc thêm wildcards để biết nó là gì, rảnh sẽ dịch luôn trong một bài khác). Được dùng rộng rãi khi thao tác với tên file. cách nhớ:
  • #: gỡ bỏ chuỗi ký tự từ trái qua.
  • %: gỡ bỏ chuỗi ký tự từ phải qua.
  • nếu có 2 dấu (## hoặc %%) thì bash sẽ bỏ phần chuổi dài nhất có thể.
VD: Phần chuỗi bị gạch là phần bị gỡ bỏ trong cột kết quả, hãy mở terminal lên và thử:
string="Be liberal in what you accept, and conservative in what you send"

Cú pháp Kết quả
${string#* } Be liberal in what you accept, and conservative in what you send
${string##* } Be liberal in what you accept, and conservative in what you send
${string% *} Be liberal in what you accept, and conservative in what you send
${string%% *} Be liberal in what you accept, and conservative in what you send
${string#*in} Be liberal in what you accept, and conservative in what you send
${string##*in} Be liberal in what you accept, and conservative in what you send

hãy chú ý thứ tự vị trí của dấu cách (space) và dấu * trong 4 ví dụ đầu, 2 ví dụ sau là thêm vào để dễ hiểu hơn. Sau này lật lại xem khỏi bị nhầm lẫn.
Ứng dụng thực tế:
Một số VD thao tác tên file:
lấy tên của file, bỏ qua phần đuôi mở rộng
${FILENAME%.*}
➡bash_hackers.txt
Lấy phần đuôi mở rộng, bỏ qua tên file
${FILENAME##*.}
bash_hackers.txt
lấy đường dẫn đến thư mục
${PATHNAME%/*}
➡/home/bash/bash_hackers.txt
Lấy tên file bỏ qua đường dẫn thư mục
${PATHNAME##*/}
/home/bash/bash_hackers.txt
Đây là những cú pháp thao tác với tên file có 1 đuôi mở rộng. Tùy vào cách sử dụng mà có thể điều chỉnh gỡ bỏ nhiều ích cho phù hợp.

Đối với mảng

Khi sử dụng trên mảng, cú pháp gỡ bỏ này sẽ có hiệu lực lên mỗi phần tử của mảng, khai triển từng phần tử hay toàn bộ mảng đều như nhau, hiệu lực này sẽ ảnh hưởng đối với từng phần tử sau khi khai triển.
VD sau sẽ gỡ bỏ "is" khỏi các phần tử có "is" tận cùng bên phải:
array=(this is a text)
echo "${array[@]%is}"
➡ Th a text (this is a text-bị gạch bỏ is)

6, TÌM KIẾM VÀ THAY THẾ

${PARAMETER/PATTERN/STRING}
${PARAMETER//PATTERN/STRING}
${PARAMETER/PATTERN}
${PARAMETER//PATTERN}
Tìm kiếm và thay thế mẫu PATTERN bằng chuỗi STRING trong giá trị của biến sau khi khai triển. Nếu không đặt STRING thì cú pháp sẽ xóa PATTERN. 2 kiểu cú pháp chỉ khác nhau số lượng dấu /. Cách nhớ:
một dấu: chỉ thay thế 1 lần.
hai dấu: thay thế toàn bộ.
VD: Thay thế từ "in" bằng từ "by" trong ví dụ dưới:
string="Be liberal in what you accept, and conservative in what you send"
form 1: thay thế 1 từ "in" đầu tiên.
${MYSTRING/in/by}
➡Be liberal inby what you accept, and conservative in what you send
form 2: thay thế toàn bộ từ "in" trong chuỗi
${MYSTRING//in/by}
➡Be liberal inby what you accept, and conservative inby what you send
Thả neo (Anchoring-dịch ra là thả neo nên để luôn): nếu mẫu xuất hiện nhiều lần trong chuỗi, mà chỉ cần thay thế 1 mẫu thì có thể quyết định thay thế mẫu tận cùng bên trái hoặc bên phải nhờ các ký tự %(percent-sign - dấu phần trăm) và # (hashmark-dấu thăng).VD
MYSTRING=xxxxxxxxxx
echo ${MYSTRING/#x/y}#Kết quả: yxxxxxxxxx
echo ${MYSTRING/%x/y}#Kết quả: xxxxxxxxxy
Nếu để trống phần thay thế sau dấu / thứ 2 của form dùng 2 dấu, sẽ tương đương form dùng 1 dấu /.
echo ${MYSTRING//conservative/}
sẽ tương đương
echo ${MYSTRING//conservative}

Đối với mảng:

Đây là cú pháp mở rộng có tác dụng lên tất cả các phần tử của mảng.Cũng giống như cú pháp chuyển đổi HOA/thường trên kia, nó hoạt động trên một giá trị phần tử hoặc từng gía trị trên toàn bộ phần tử mảng (khi sử dụng * hoặc @).
VD: thay thế t thành d (khác với source, em thay chữ T trong This thành t cho dễ thấy) :
array=(this is a text)
echo "${array[@]/t/d}"
➡dhis is a dext
echo "${array[@]//t/d}"
➡dhis is a dexd

7, ĐỘ DÀI CỦA CHUỖI.

${#PARAMETER}
Dùng cái này nó sẽ cho biết số lượng kí tự trong giá trị của tham số sau khi khai triển.
VD:
MYSTRING="Be liberal in what you accept, and conservative in what you send"
echo ${#MYSTRING}
Kết quả là: 64
Độ dài này được tính bằng số lượng ký tự chứ không phải bytes. Nếu tính bằng bytes thì còn tùy thuộc vào môi trường mà ra kết quả khác nhau (VD multibyte-characters, ký tự chứa nhiều bytes của dạng mã hóa UTF8)
Chả có gì nhiều để nói về nó nhỉ ?

Đối với mảng:

Sẽ có khác nhau giữa từng phần tử và toàn bộ mảng:
  • Đối với một phần tử: cũng như tham số thường trên kia, nó sẽ cho biết số lượng ký tự trong giá trị phần tử đó.
  • Đối với toàn bộ mảng: nó sẽ cho biết số lượng phần tử được set của mảng, giá trị phần tử là rỗng vẫn tính nhưng nếu nó bị unset thì thôi.
VD:
array=(This is a text)
echo ${#array[1]}
➡2 #từ is có 2 ký tự.
echo ${#array[@]}
➡4 #mảng có 4 phần tử được set.
array[1]="" #đặt phần tử 1 là rỗng.
echo ${#array[@]}
➡4 #vẫn là 4
unset array[1] #unset phần tử 1
echo ${#array[@]}
➡3
Chú ý: trên kia nói rồi, nhắc lại thôi. Do phần tử bị unset sẽ mất luôn số thứ tự trong mảng nên số index lớn nhất không phải là số lượng phần tử trong mảng. 1 cái mảng với số index lõm tùm lum như 2 6 7 là khả thi đối với bash, Vì thế không thể sử dụng vòng lặp xuyến suốt mảng với số index này.

8, KHAI TRIỂN 1 PHẦN CHUỖI TRONG GIÁ TRỊ (SUBSTRING EXPANSION).

Hồi nãy là cách gỡ bỏ 1 phần chuỗi, còn đây là giữ lấy 1 phần chuỗi của giá trị.
${PARAMETER:OFFSET}
${PARAMETER:OFFSET:LENGTH}
Cú pháp này hoạt động như sau: chọn 1 điểm mốc (OFFSET) và độ dài (LENGHT) của chuỗi con. Nó sẽ khai triển từ điểm mốc đó (giữ luôn điểm mốc lại) đến khi đủ số lượng ký tự độ dài. Nếu để trống độ dài, nó sẽ khai triển đến hết giá trị.
OFFSET và LENGTH có thể là bất kỳ biểu thức đại số nào ở đây link .
CHÚ Ý: OFFSET BẮT ĐẦU TỪ 0 CHỨ KHÔNG PHẢI 1, KÝ TỰ Ở VỊ TRÍ OFFSET CHỈ RA ĐƯỢC GIỮ LẠI. (theo đúng source thì là vậy, nhưng nếu tính offset bắt đầu từ số 1 thì offset sẽ trở thành số lượng ký tự bị mất, hiểu theo cách nào tùy mỗi người, nhưng tí nữa em sẽ phụ chú thêm 1 cái bảng tóm tắt hiểu theo kiểu này cho dễ nhớ, ai không thích cứ hiểu theo cách của source, còn nếu cách hiểu của source là bắt buộc thì hãy cmt phía dưới để em xóa bỏ)
Còn đây là phần dịch của source với ví dụ
MYSTRING="Be liberal in what you accept, and conservative in what you send"

TH1: Chỉ sử dụng duy nhất offset:
Trường hợp này chỉ sử dụng số offset, bỏ qua số lenght, chú ý offset được tính từ 0.
echo ${MYSTRING:34}
Be liberal in what you accept, and conservative in what you send (gạch 34 ký tự đầu)

TH2: Sử dụng cả offset và lenght:
Trường hợp này có thêm lenght, chỉ ra số lượng ký tự được giữ lại.
echo ${MYSTRING:34:13}
Be liberal in what you accept, and conservative in what you send (gạch 34 ký tự đầu, giữ lại 13 ký tự tiếp theo, gạch bỏ phần còn lại)

TH3: sử dụng offset là số âm:
Nếu offset là số âm, thì sẽ được đếm từ phải qua. ký tự offset chỉ ra và phần bên phải nó sẽ được giữ lại, offset lần này đếm từ -1 (rồi chữa ? :))) còn lenght vẫn là số lượng ký tự được giữ lại đếm từ mốc offset qua phải như cũ.
Chú ý: giữa dấu :(colon) và dấu -(negative) phải có khoảng trắng, hoặc đặt dấu âm vào ngoặc (). Để phân biệt với "cú pháp khai triển giá trị mặc định" ở dưới sẽ trình bày.
${MYSTRING: -10:5}
${MYSTRING:(-10):5}

TH4: sử dụng lenght là số âm:
Nếu lenght là số âm, cũng giống như offset là số âm, sẽ được đếm tứ phải qua trái, chỉ khác nó là số lượng ký tự bị bỏ đi. việc khai triển sẽ từ vị trí offset đến phần còn lại.
echo "${MYSTRING:11:-17}"
Be liberal in what you accept, and conservative in what you send (gạch bỏ 11 ký tự đầu và 17 ký tự cuối)
Cái này có thể dùng từ Bash 4.2-alpha,xem thêm những thay đổi của bash.(link http://wiki.bash-hackers.org/scripting/bashchanges)

Xong phần dịch của source, bây giờ sẽ là tổng hợp cho dễ nhớ (cứ dùng nhiều tự khắc nhớ thôi):
hãy quan niệm, offset và lenght đều là số đếm ký tự (không giống suorce, vị trí rồi số 0 này nọ) ta có cái bảng tóm tắt này, các bác hãy kiểm nghiệm xem đúng không ?

Số Dương Số Âm
offset ký tự mất, từ tráiký tự còn, từ phải
lenght ký tự còn, từ tráiký tự mất, từ phải
Rất dễ nhớ phải không ? số lượng ký tự còn lại và số lượng ký tự mất đi. Đếm từ trái qua và đếm từ phải qua.

Đối với mảng:

Được chia làm 2 trường hợp:
  • Đối với một phần tử: thì như trên kia, không khác biến thường.
  • Đối với toàn mảng (dùng ký tự @ và *): các con số sẽ đại diện cho phần tử, chứ không còn đại diện ký tự nữa, phần được khai triển trong toàn mảng được xác định bởi 2 con số: số đầu tiên chính là số index chỉ vị trí phần tử bắt đầu được giữ lại (số index bắt đầu từ số 0) và số tiếp theo là số lượng phần tử được khai triển.
Phần dịch theo source tới đây thôi, thêm 1 tí là đối với mảng các con số trong cú pháp cũng có thể là số âm, công dụng cũng giống trên kia (chỉ khác thay ký tự là phần tử thôi) tất nhiên hoàn toàn có thể sử dụng cái bảng tóm tắt trên kia cho dễ nhớ, chỉ thay đổi là thay vì số lượng ký tự trong chuỗi sẽ trở thành số lượng phần tử trong mảng.
VD:
array=(This is a text)
echo ${array[0]:2:2}
⇒ is (từ "is" trong "This", của phần tử 0)
echo ${array[@]:1:2}
⇒ is a (bắt đầu giữ lại từ phần tử 1 và giữ lại 2 phần tử là 1 và 2)

9, KHAI TRIỂN MỘT GIÁ TRỊ MẶC ĐỊNH

${PARAMETER:-WORD}
${PARAMETER-WORD}
Ở form 1, nếu PARAMETER chưa được set (unset) hoặc là biến rỗng (null) thì sẽ cho ra kết quả mặc định là WORD, nếu set giá trị rồi thì sẽ khai triển PARAMETER như bình thường (như thế này ${PARAMETER}). Khác biệt giữa 2 form là dấu :(colon), nếu KHÔNG có dấu :, WORD chỉ được khai triển khi PARAMETER unset, còn biến rỗng (null) vẫn khai triển ra giá trị rỗng như bình thường.
VD:
echo "Your home directory is: ${HOME:-/home/$USER}."
echo "${HOME:-/home/$USER} will be used to store your personal data."
Nếu user chưa có home folder thì vẫn hiện đường dẫn đến dù chưa có.
#!/bin/bash
read -p "Enter your gender (just press ENTER to not tell us): " GENDER
echo "Your gender is ${GENDER:-a secret}."
Nếu không nhập GENDER thì sẽ hiện ra "Your gender is a secret". Chú ý: cú pháp này chỉ khai triển ra WORD trong lần sử dụng chứ không gán giá trị WORD cho biến PARAMETER, biến PARAMETER vẫn sẽ có giá trị là unset (hoặc null).

Đối với mảng:

Cũng giống những cái cú pháp trên, sẽ có sự khác nhau khi áp dụng cú pháp này trên mỗi phần tử (chỉ định số index) và trên toàn bộ phần tử (dùng * và @):
  • Đối với 1 phần tử lẻ được chỉ định số index rõ ràng, cũng giống như cú pháp này với biến bình thường, nếu phần tử unset hoặc null (null có được khai triển hay không thì tùy vào :- hay -, giống trên kia luôn) thì sẽ khai triển đoạn chuỗi mặc định WORD.
  • Đối với toàn bộ mảng khi dùng * và @, chuỗi mặc định WORD sẽ được khai triển nếu toàn bộ phần tử trong mảng là unset (hoặc null nếu dùng :- trong cú pháp), và sẽ khai triển toàn bộ mảng như bình thường nếu có phần tử được set (hoặc null nếu dùng - trong cú pháp pháp). Tức là không khác trên kia chỉ là mở rộng ra toàn bộ mảng thôi. 
Ví dụ rất dài, nhưng cũng ráng copy vào, các bác nên bật terminal thử để kiếm chứng.
VD:
####
# Example cases for unset/empty arrays and nullstring elements
####
### CASE 1: Unset array (no array)
# make sure we have no array at all
unset array
echo ${array[@]:-This array is NULL or unset}
echo ${array[@]-This array is NULL or unset}
### CASE 2: Set but empty array (no elements)
# declare an empty array
array=()
echo ${array[@]:-This array is NULL or unset}
echo ${array[@]-This array is NULL or unset}
### CASE 3: An array with only one element, a nullstring
array=("")
echo ${array[@]:-This array is NULL or unset}
echo ${array[@]-This array is NULL or unset}
### CASE 4: An array with only two elements, a nullstring and a normal word
array=("" word)
echo ${array[@]:-This array is NULL or unset}
echo ${array[@]-This array is NULL or unset}
Thôi cũng cố làm 1 cái bảng tóm tắt cho dễ nhớ, sau này xem lại không cần nhìn cái wall of text trên kia.
unset null
:- WORDWORD
- WORD$PARAMETER

10, KHAI TRIỂN VÀ GÁN 1 GIÁ TRỊ MẶC ĐỊNH CHO BIẾN:

${PARAMETER:=WORD}
${PARAMETER=WORD}
Cũng giống như cú pháp khai triển giá trị mặc định cho biến trên kia, nhưng không chỉ khai triển khi biến unset (hoặc null), cú pháp này sẽ đặt luôn giá trị mặc định này cho biến. Dấu :(colon) cũng tạo ra sự khác biệt khi biến là null giữa 2 form (giống cú pháp kia luôn).
VD:
echo "Your home directory is: ${HOME:=/home/$USER}."
echo "$HOME will be used to store your personal data."
sau khi chạy VD trên sẽ thấy biến HOME đã được set và gán giá trị mặc định.
hãy tạo 1 script với thí dụ bên dưới:
#!/bin/bash
read -p "Enter your gender (just press ENTER to not tell us): " GENDER
echo "Your gender is ${GENDER:=a secret}."
echo "Ah, in case you forgot, your gender is really: $GENDER"

Đối với mảng:

Sẽ có khác biệt với cú pháp này, đối với từng phần tử của mảng sẽ không có gì khác khi với biến thông thường. Nhưng, đối với toàn bộ mảng (dùng @ hoặc *) thì cúp pháp này sẽ KHÔNG HOẠT ĐỘNG, bởi vì không thể gán giá trị mặc định cho chúng. Chạy thử thì báo lỗi như này "-bash: i[@]: bad array subscript".

11, KHAI TRIỂN MỘT GIÁ TRỊ THAY THẾ.

${PARAMETER:+WORD}
${PARAMETER+WORD}
Ngược lại với cú pháp giá trị mặc định trên kia, nó sẽ khai triển ra giá trị thay thế WORD nếu như biến PARAMETER có giá trị. Còn nếu là unset hoặc null thì nó sẽ không khai triển ra gì cả.VD:
echo "The Java application was installed and can be started.${JAVAPATH:+ NOTE: JAVAPATH seems to be set}"
ví dụ trên sẽ đưa ra 1 cảnh báo nếu biến JAVAPATH được set (vì nó có thể ảnh hưởng tới quá trình khởi động của một ứng dụng giả định nào đó)
Trong các ví dụ giả định tiếp theo, sẽ xem tình trạng của các cờ (lập trình nhiều thì chắc các bác biết cờ là cái gì rồi), và sẽ in ra 1 dòng cảnh báo nếu cờ được set.
#!/bin/bash
read -p "If you want to use special flags, enter them now: " SPECIAL_FLAGS
echo "The installation of the application is finished${SPECIAL_FLAGS:+ (NOTE: there are special flags set: $SPECIAL_FLAGS)}."
Dấu :(colon) cũng tạo ra sự khác nhau giữa 2 form khi biến là rỗng, khi thiếu dấu : (chính là form 2) , biến là null thì cú pháp sẽ khai triển ký tự thay thế.
chạy script dưới để kiểm tra:
# test that with the three stages:
# unset foo
# foo=""
# foo="something"
if [[ ${foo+isset} = isset ]]; then
echo "foo is set..."
else
echo "foo is not set..."
fi

Đối với mảng:

Như hầu hết cú pháp:
  • Đối với MỘT phần tử của mảng, cú pháp này hoạt động bình thương như đối với biến thông thường.
  • Đối với toàn bộ mảng (dùng @ và *), cú pháp sẽ cho ra chuỗi thay thế nếu trong mảng có ít nhất một phần tử chứa giá trị hoặc null (null có được khai triển hay không thì tùy vào :+ hay + nhé).

12, CÚ PHÁP BÁO LỖI NẾU NULL HOẶC UNSET

${PARAMETER:?WORD}
${PARAMETER?WORD}
Nếu biến PARAMETER có giá trị hoặc đã được set, cú pháp này sẽ khai triển như thông thường. Nhưng nếu PARAMETER unset hoặc null, thì WORD sẽ được thêm vào phần thông báo lỗi của bash.
$ echo "The unset parameter is: ${p_unset?not set}"
bash: p_unset: not set
Vậy tại sao không dùng luôn cú pháp khai triển giá trị mặc định trên kia ? khác biệt ở chỗ, nếu khai triển ra thông báo lỗi này, giá trị biến báo trạng thái exit $? của lệnh vừa thực thi sẽ khác 0. ( biến ? là cái gì thì xem ở đây link). Cụ thể:
  • Đối với shell có tương tác (cái bảng đen các bác gõ vào trên 1 cái tty): giá trị biến báo trạng thái exit $? sẽ khác 0.
  • Đối với shell không có tương tác ( là các bác viết nó thành script rồi chạy): mã exit khi kết thúc script sẽ khác 0.
nếu lăn tăn shell có tương tác (interactive shells) và shell không tương tác ( non-interactive shells) thì có thể đọc thêm ở đây: link.
Và dấu :(colon) cũng có tác dụng tương tự khi biến là null.
  • có dấu : trong cú pháp (:?): biến null cũng khai triển ra báo lỗi.
  • không có dấu : trong cú pháp (?): biến null được khai triển bình thường.
-----------------------------------------------------------
PARAMETER EXPANSION tới đây là hết nước hết cái rồi, phần sau là Bugs and Portability considerations. Cảm thấy cần cứ đọc thêm cho biết.
p/s: sai chỗ nào góp ý chỉnh sửa dùm, đừng đọc xong xách đít ra đi. Do đết biết HTML nên trình bày không bằng cái source mọi người thông cảm, mà mục đích chính để ghi nhớ vàgiúp mọi người chỉ là phụ nên chả quan trọng mấy. 😉😊
Vừa mua 1 con card màn hình, kế tiếp sẽ dịch cái tut hướng dẫn cài driver NVIDIA.