Tìm kiếm Blog này

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.