Bash Brackets

Bash punya banyak jenis tanda kurung. Misalnya, banyak sekali. Penggandaan tanda kurung yang berbeda menambah makna, dan tanda dolar di depan tanda kurung memiliki arti yang jauh lebih berbeda. Dan, tanda kurung tersebut digunakan secara berbeda dibandingkan banyak bahasa pemrograman lain.

Catatan kecil untuk semua ini adalah Bash umumnya akan melihat spasi di antara tanda kurung bulat atau siku dan apa pun yang ada di dalamnya. Ini berbeda dengan spasi di antara tanda kurung kurawal.

( Single Parentheses )

Tanda kurung tunggal akan menjalankan perintah di dalam subshell. Artinya, perintah tersebut akan menjalankan semua perintah di dalamnya, lalu mengembalikan satu kode keluar. Setiap variabel yang dideklarasikan atau perubahan lingkungan akan dibersihkan dan dihapus. Karena berada di dalam subshell, jika Anda menempatkannya di dalam loop, perintah tersebut akan berjalan sedikit lebih lambat daripada jika Anda memanggil perintah tanpa tanda kurung.

a='This string'
( a=banana; mkdir $a )
echo $a
# => 'This string'
ls
# => ...
# => banana/

(( Double Parentheses ))

Ini untuk digunakan dalam aritmatika integer. Anda dapat melakukan penugasan, operasi logika, dan operasi matematika seperti perkalian atau modulo di dalam tanda kurung ini. Namun, perlu diketahui bahwa tidak ada keluaran. Setiap perubahan variabel yang terjadi di dalamnya akan tetap ada, tetapi jangan berharap dapat menetapkan hasilnya ke apa pun. Jika hasil di dalamnya bukan nol, kode keluar akan mengembalikan nol (berhasil). Jika hasil di dalamnya nol, kode keluar akan mengembalikan 1.

i=4
(( i += 3 ))
echo $i
# => 7
(( 4 + 8 ))
# => No Output
echo $?  # Check the exit code of the last command
# => 0
(( 5 - 5 ))
echo $?
# => 1

# Strings inside get considered 'zero'.
(( i += POO ))
echo $i
# => 7

# You can't use it in an expression
a=(( 4 + 1 ))
# => bash: syntax error near unexpected token '('

<( Angle Parentheses )

Ini dikenal sebagai process substitution. Mirip dengan pipe, bedanya bisa digunakan dimanapun perintah yang membutuhkan argumen file. Dan bisa digunakan beberapa sekaligus!

sort -nr -k 5 <( ls -l /bin ) <( ls -l /usr/bin ) <( ls -l /sbin )

# => Like a billion lines of output that contain many of the
# => executables on your computer, sorted in order of descending size.

# Just in case you don't magically remember all bash flags,
# -nr  means sort numerically in reverse (descending) order
# -k 5 means sort by Kolumn 5.  In this case, for `ls -l`, that is the "file size"

Ini berfungsi karena perintah sort memperlukan satu atau beberapa nama filename sebagai argumen. Dibalik layar, <( stuff ) sebenarnya menampilkan temporary file (berkasi pipe tanpa nama) yang akan digunakan oleh peritah sort

Contoh lain yang menunjukkan kegunaannya adalah penggunaan perintah comm, yang menampilkan baris-baris yang sama pada berkas-berkas tersebut. Karena comm perlu mengurutkan berkas-berkas inputnya, Anda dapat melakukan hal berikut:

# The lame way
sort file1 > file1.sorted
sort file2 > file2.sorted
comm -12 file1.sorted file2.sorted

Atauuu, bisa menjadi total BAshMF dan melakukannya dengan cara ini:

# The baller way
comm -12 <( sort file1 ) <( sort file2 )

$( Dollar Single Parentheses )

Ini untuk menginterpolasi keluaran perintah subshell ke dalam sebuah string. Perintah di dalamnya dijalankan di dalam subshell, lalu setiap keluarannya ditempatkan ke dalam string apa pun yang sedang Anda buat.

intro="My name is $( whoami )"
echo $intro
# => My name is ryan

# And just to prove that it's a subshell...
a=5
b=$( a=1000; echo $a )
echo $b
# => 1000
echo $a
# => 5

$( Dollar Single Parentheses Dollar Q )$?

Jika Anda ingin menginterpolasi suatu perintah, tetapi hanya kode keluarnya saja dan bukan nilainya, inilah yang Anda gunakan.

if [[ $( grep -q PATTERN FILE )$? ]]
then
  echo "Dat pattern was totally in dat file!"
else
  echo "NOPE."
fi

Meskipun, sebenarnya, ini bukan pola kurung siku khusus, melainkan penggunaan $? yang menarik, karena pola di atas tetap berfungsi meskipun ada spasi di antara $( stuff ) dan $?. Namun, ini tetap merupakan tips yang bagus.

$(( Dollar Double Parentheses ))

Ingat bagaimana (( Double Parentheses )) tidak menghasilkan apa-apa? Ingat bagaimana itu agak menyebalkan? Nah, Anda bisa menggunakan $(( Double Parentheses )) untuk melakukan interpolasi aritemetika, yang merupakan cara lebih canggih untuk mengatakan, "Masukan hasil keluaran ke dalam string ini".

a=$(( 16 + 2 ))
message="I don't want to brag, but I have like $(( a / 2 )) friends."
echo $message
# => I don't want to brag, but I have like 9 friends."

b=$(( a *= 2 ))         # You can even do assignments.  The last value calculated will be the output.
echo $b
# => 36
echo $a
# => 36

Satu hal yang perlu diingat adalah bahwa ini sepenuhnya merupakan aritmatika integer. Tidak ada desimal. Gunakan bc untuk perhitungan floating point.

echo $(( 9 / 2 ))  # You might expect 4.5
# => 4

echo $(( 9 / 2.5 ))
# => bash: 9 / 2.5 : syntax error: invalid arithmetic operator (error token is ".5 ")

[ Single Square Brackets ]

Ini adalah versi alternatif dari pengujian bawaan. Perintah-perintah di dalamnya dijalankan dan diperiksa "kebenarannya". String of zero lenghth bernilai salah. String dengan panjang satu atau lebih (meskipun karakter tersebut berupa spasi) bernilai benar.

if [ -f my_friends.txt ]
then
    echo "I'm so loved!"
else
    echo "I'm so alone."
fi

Satu hal terakhir yang penting untuk diperhatikan adalah bahwa test dan [ sebenarnya adalah perintah shell. [[ ]] sebenarnya adalah bagian dari bahasa shell itu sendiri. Artinya, isi di dalam Kurung Siku Ganda tidak diperlakukan seperti argumen. Alasan Anda menggunakan Kurung Siku Tunggal adalah jika Anda perlu melakukan pemisahan kata atau perluasan nama berkas.

Berikut ilustrasi perbedaannya. Katakanlah Anda menggunakan Kurung Siku Ganda dengan cara berikut.

[[ -f *.txt ]]
echo $?
# => 1

Salah, tidak ada berkas yang secara eksplisit bernama "[asterisk].txt". Mari kita asumsikan saat ini tidak ada berkas .txt di direktori kita.

# If there's no files .txt files:
[ -f *.txt ]; echo $?
# => 1

*.txt diekspansi menjadi string kosong, yang bukan merupakan berkas, lalu pengujian dievaluasi. Mari kita buat berkas txt.

touch cool_beans.txt
# Now there's exactly one .txt file
[ -f *.txt ]; echo $?
# => 0

*.txt diekspansi menjadi daftar nama file yang cocok, dipisahkan spasi: "cool_beans.txt", lalu pengujian dievaluasi dengan satu argumen tersebut. Karena file tersebut ada, pengujian berhasil. Namun, bagaimana jika ada dua file?

touch i_smell_trouble.txt  # bean pun.  #sorrynotsorry
# Now there's two files
[ -f *.txt ]
# => bash: [: too many arguments.

*.txt diekspansi menjadi "cool_beans.txt i_smell_trouble.txt", lalu pengujian dievaluasi. Bash menghitung setiap nama berkas sebagai argumen, menerima 3 argumen, bukan dua seperti yang diharapkan, dan mengaburkannya.

Sekadar untuk menegaskan maksud saya: meskipun saat ini ada dua berkas .txt, pengujian berikutnya ini tetap gagal.

[[ -f *.txt ]]; echo $?
# => 1.  There is still no file called *.txt

Saya mencoba memberikan beberapa contoh mengapa Anda menginginkan ini, tetapi saya tidak dapat menemukan contoh yang realistis.

Secara umum, sepertinya, aturan praktis yang baik adalah: jika Anda perlu menggunakan test atau [ ], Anda akan mengetahuinya. Jika Anda tidak yakin apakah Anda membutuhkannya, Anda mungkin tidak membutuhkannya dan Anda mungkin sebaiknya menggunakan [[ double square brackets ]] untuk menghindari banyak jebakan rumit dari perintah test. Jika shell Anda cukup modern untuk memilikinya.

[[ Double Square Brackets ]]

Pengujian True/False. Baca bagian di atas untuk penjelasan perbedaan antara tanda kurung siku tunggal dan ganda. Selain itu, tanda kurung siku ganda mendukung pencocokan ekspresi reguler yang diperluas. Gunakan tanda kutip di sekitar argumen kedua untuk memaksakan pencocokan mentah, alih-alih pencocokan regex.

pie=good
[[ $pie =~ d ]]; echo $?
# => 0, it matches the regex!

[[ $pie =~ [aeiou]d ]]; echo $?
# => 0, still matches

[[ $pie =~ [aei]d ]]; echo $?
# => 1, no match

[[ $pie =~ "[aeiou]d" ]]; echo $?
# => 1, no match because there's no literal '[aeoiu]d' inside the word "good"

Selain itu, di dalam tanda kurung siku ganda, < dan > diurutkan berdasarkan lokal Anda. Di dalam tanda kurung siku tunggal, urutannya berdasarkan urutan pengurutan mesin Anda, yang biasanya ASCII.

{Single Curly Braces}

Kurung kurawal tunggal digunakan untuk ekspansi.

echo h{a,e,i,o,u}p
# => hap hep hip hop hup
echo "I am "{cool,great,awesome}
# => I am cool I am great I am awesome

mv friends.txt{,.bak}
# => braces are expanded first, so the command is `mv friends.txt friends.txt.bak`

Kerennya, kamu juga bisa membuat rentang! Dengan angka nol di depan!

echo {01..10}
01 02 03 04 05 06 07 08 09 10
echo {01..10..3}
01 04 07 10

${dollar braces}

Perhatikan bahwa tidak ada spasi di sekitar konten. Ini untuk interpolasi variabel. Anda menggunakannya ketika interpolasi string normal bisa terasa aneh.

# I want to say 'bananaification'
fruit=banana
echo $fruitification
# => "" No output, because $fruitification is not a variable.
echo ${fruit}ification
# => bananaification

Hal lain yang bisa Anda gunakan dengan ${Dollar Braces} adalah manipulasi variabel. Berikut beberapa penggunaan umum.

Menggunakan nilai default jika variabel tidak didefinisikan.

function hello() {
  echo "Hello, ${1:-World}!"
}
hello Ryan
# => Hello Ryan!
hello
# => Hello World!

function sign_in() {
    name=$1
  echo "Signing in as ${name:-$( whoami )}"
}
sign_in
# => Signing in as ryan
sign_in coolguy
# => Signing in as coolguy

Mendapatkan panjang suatu variabel.

name="Ryan"
echo "Your name is ${#name} letters long!"
# => Your name is 4 letters long!

Memotong bagian-bagian yang sesuai dengan pola.

Anda dapat menggunakan huruf kapital untuk mencocokkannya!

echo ${url^^a}
# => https://AssertnotmAgic.com/About

Anda bisa mendapatkan potongan strings.

echo ${url:2:5}  # the pattern is ${var:start:len}.  Start is zero-based.
# => tps://

Anda dapat mengganti pattern.

echo ${url/https/ftp}
# => ftp://assertnotmagic.com

# Use a double slash for the first slash to do a global replace
echo ${url//[aeiou]/X}
# => https://XssXrtnXtmXgXc.cXm

Dan, Anda dapat menggunakan variabel secara tidak langsung sebagai nama variabel lainnya.

function grades() {
  name=$1
  alice=A
  beth=B
  charles=C
  doofus=D
  echo ${!name}
}

grades alice
# => A
grades doofus
# => D
grades "Valoth the Unforgiving"
# => bash: : bad substitution.
# There is no variable called Valoth the Unforgiving,
# so it defaults to a blank value.
# Then, bash looks for a variable with a name of "" and errors out.

<<Double Angle Heredocs

Beginilah cara membuat string multiline di Bash (satu metode). Dua panah lalu sebuah kata—kata apa pun yang Anda pilih—untuk menandai awal string. String tidak akan berakhir sampai Anda mengulangi kata ajaib Anda.

read -r -d '' nice_message <<MESSAGE
Hi there!  I really like the way you look
when you are teaching newbies things
with empathy and compassion!
You rock!
MESSAGE

echo "$nice_message"
# => Hi there!  I really like the way you look
# => when you are teaching newbies things
# => with empathy and compassion!
# => You rock!

# Note, if you store a string with newlines in a variable,
# you have to quote it to get the newlines to actually print.
# If you just use `echo $nice_message`, it won't reflect newlines.

Kata tersebut bisa apa saja yang Anda inginkan. Saya biasanya menggunakan "HEREDOC" agar lebih mudah digunakan nanti.

Trik terakhir adalah, jika Anda menambahkan tanda hubung setelah tanda panah, tab awal (kecuali spasi) di heredoc Anda akan dihapus.

cat <<-HEREDOC
        two leading tabs
    one leading tab
  two spaces here
HEREDOC

# => two leading tabs
# => one leading tab
# =>   two spaces here

Punctuation's a Killer

Semoga ini bermanfaat. Jika Anda melihat sesuatu yang terlewat atau memiliki kegunaan menarik lainnya untuk salah satu varian ini, beri tahu saya, dan saya akan memperbaruinya serta memuji kejeniusan Anda di depan umum. Terima kasih sudah membaca!