Subshells & Process Substitution — Terminal

Subshells & Process Substitution Shell menyediakan beberapa cara menggabungkan perintah — subshell, grouped commands, command substitution, dan process substitu

Subshells & Process Substitution

Shell menyediakan beberapa cara menggabungkan perintah — subshell, grouped commands, command substitution, dan process substitution. Paham perbedaannya menutup banyak "kenapa variabel saya hilang?" dan membuka pola yang elegan untuk tugas kompleks.

Subshell ( ... )

Kurung biasa menjalankan perintah di subshell — proses baru yang meng-inherit environment tapi perubahannya tidak berefek ke parent:

(cd /tmp && ls)
# cd terjadi di subshell saja — kamu masih di direktori semula

pwd
# masih direktori asal, bukan /tmp

# Variabel juga terisolasi
X=1
(X=99; echo "inside: $X")    # inside: 99
echo "outside: $X"             # outside: 1

Kapan pakai subshell:

# Simpan + restore env dengan mudah
(
    export DEBUG=1
    export PATH="/custom:$PATH"
    ./flaky-program
)
# Kembali ke PATH normal otomatis

Grouped Commands { ... ; }

Kurung kurawal tidak membuat subshell — perintah jalan di shell saat ini:

{ cd /tmp && ls ; }          # cd BENERAN pindah
pwd                           # /tmp sekarang

X=1
{ X=99; echo "inside: $X"; }  # inside: 99
echo "outside: $X"            # outside: 99  (berubah!)

Syntax picky: butuh spasi setelah { dan titik koma / newline sebelum }.

Kapan pakai grouped:

{ echo "=== Header ==="; date; uname -a; } > info.txt
# Tanpa kurung, hanya uname yang ke-redirect

Command Substitution $( ... ) vs Backtick

Ambil output perintah dan sisipkan sebagai string:

TODAY=$(date +%Y-%m-%d)
echo "Hari ini: $TODAY"

FILES=$(ls | wc -l)
echo "$FILES file di direktori ini"

Sintaks lama pakai backtick `cmd`. Hindari$() lebih enak karena:

  1. Bisa di-nest$(outer $(inner)) vs `outer \`inner\ `` (harus escape).
  2. Lebih mudah dibaca — backtick mudah terlihat seperti single quote.
  3. Quoting lebih waras — kurang jebakan escape.
# Nested: modern jelas
BRANCH=$(git rev-parse --abbrev-ref $(git symbolic-ref HEAD))

# Backtick: butuh escape berlapis, mudah salah
BRANCH=`git rev-parse --abbrev-ref \`git symbolic-ref HEAD\``

Process Substitution <(cmd) dan >(cmd)

Pola paling powerful dari seksi ini — perintah diperlakukan seakan-akan file.

<(cmd) — Output sebagai File

Shell buat file khusus (biasanya /dev/fd/63) yang berisi output perintah. Cocok untuk perintah yang butuh argumen file tapi kamu punya stream:

# Bandingkan output dua perintah
diff <(sort a.txt) <(sort b.txt)
# tanpa ini: harus buat file temp dulu

# Compare dua branch git
diff <(git show main:config.json) <(git show feature:config.json)

# grep dua sumber bersamaan
grep -F -f <(cat important-lines.txt) haystack.txt

# Feed output ke command yang baca argv
wc -l <(find . -name "*.js")

>(cmd) — Input sebagai Proses

Kebalikannya — sesuatu yang menulis ke "file" sebenarnya mengirim ke stdin perintah:

# tee ke banyak pipeline
ls -la | tee >(grep "\.log" > logs.txt) >(grep "\.js" > jsfiles.txt) > all.txt
# Satu ls → split ke tiga output berbeda

# Hash checksum sambil copy
tar -cz data/ | tee >(sha256sum > data.tar.gz.sha256) > data.tar.gz

Cek dukungan:

Named Pipe (mkfifo)

Untuk komunikasi antar proses yang tahan lama (tidak sekali pakai seperti |):

mkfifo /tmp/mypipe

# Terminal 1: tulis
echo "hello" > /tmp/mypipe

# Terminal 2: baca
cat /tmp/mypipe
# → hello

Penulis akan blok sampai ada yang baca, dan sebaliknya. Berguna untuk IPC sederhana antar script. Process substitution sebenarnya jalan di atas konsep ini di balik layar.

Kapan Pakai Apa — Cheat Sheet

Pola Subshell? Ubah parent? Cocok untuk
(cmd; cmd) Ya Tidak Isolasi cd/env/variabel
{ cmd; cmd; } Tidak Ya Group redirect output
$(cmd) Ya Tidak Ambil string dari output
<(cmd) Ya Tidak Kasih output ke command yang baca file
>(cmd) Ya Tidak Split output ke banyak consumer
mkfifo Tidak Tidak IPC antar proses terpisah

Contoh Praktis Gabungan

# Cek diff dua API response
diff <(curl -s https://api.example.com/v1/users | jq -S .) \
     <(curl -s https://api.example.com/v2/users | jq -S .)

# Backup folder sambil hitung hash — tanpa file temp
tar -cz -C / etc/ | tee >(sha256sum > backup.sha256) | gzip > backup.tar.gz

# Install + save log tanpa kehilangan output di terminal
apt install nginx 2>&1 | tee >(grep -i error > install-errors.log)

# Jalankan migration di subshell supaya env bersih
(
    export NODE_ENV=production
    export DATABASE_URL="postgres://..."
    npm run migrate
)
# NODE_ENV & DATABASE_URL kembali ke nilai sebelumnya

Tips

  1. Default ke { ... } untuk group, pakai ( ... ) hanya saat butuh isolasi.
  2. Selalu $() bukan backtick — lebih aman untuk nested.
  3. diff <(sort a) <(sort b) adalah pola emas untuk bandingkan dua stream.
  4. tee >(...) jauh lebih rapi dari redirect + file temp untuk split output.
  5. Ingat: | otomatis membuat subshell di bash. while read x; do ...; done < file lebih aman dari cat file | while read, karena yang kedua subshell kehilangan perubahan variabel.

Yang akan kamu pelajari