"Variabilisasikan" ampersand (latar belakang proses)

9

Saya ingin tahu apakah ada cara untuk meletakkan ampersand dalam variabel dan masih menggunakannya untuk mengirim proses ke latar belakang.

Ini bekerja:

BCKGRND=yes
if [ "$BCKGRND" = "yes" ]; then
    sleep 5 &
else
    sleep 5
fi

Tapi bukankah itu keren untuk mencapai lima baris hanya dengan satu? Seperti itu:

BCKGRND='&'
sleep 5 ${BCKGRND}

Tapi itu tidak berhasil. Jika BCKGRND tidak disetel berfungsi - tetapi ketika disetel itu diartikan sebagai '&' literal dan keluar kesalahan.

BrowncoatOkie
sumber
setelah menggunakan trailing ampersand, echo $!mengembalikan PID
noobninja

Jawaban:

9

Itu tidak mungkin untuk menggunakan variabel untuk latar belakang panggilan karena ekspansi variabel terjadi setelah baris perintah diuraikan untuk operator kontrol (seperti &&dan &).

Namun pilihan lain adalah untuk membungkus panggilan dalam suatu fungsi:

mayberunbg() {
  if [ "$BCKGRND" = "yes" ]; then
    "$@" &
  else
    "$@"
  fi
}

... lalu atur variabel sesuai kebutuhan:

$ BCKGRND=yes mayberunbg sleep 3
[1] 14137
$
[1]+  Done                    "$@"
# or
$ BCKGRND=yes
$ mayberunbg sleep 3
[1] 14203
$
[1]+  Done                    "$@"
$ BCKGRND=no mayberunbg sleep 3
# 3 seconds later
$
Jeff Schaller
sumber
Apa tidak ed? +1, ini adalah solusi terbersih.
Stephen Kitt
LOL @StephenKitt; sekarang roda gigi berputar
Jeff Schaller
Saya menyukai jawaban eval karena kesederhanaannya tetapi perintah dunia nyata saya yang saya ingin latar belakang terlalu rumit dengan terlalu banyak variabel di dalamnya untuk merasa nyaman menggunakan eval. @ jeff-schaller memberikan jawaban yang mengarahkan saya ke arah yang saya tuju. Alih-alih fungsi, saya menempatkan seluruh perintah dalam variabel kemudian menggunakan gaya pernyataan if-nya untuk mengeksekusi perintah dengan atau tanpa &.
BrowncoatOkie
11

Anda dapat membalik hal-hal dan membuat variasi "foregrounding":

FOREGROUND=fg
sleep 5 & ${FOREGROUND}

Setel FOREGROUNDke trueatau kosongkan untuk menjalankan proses di latar belakang. (Pengaturan FOREGROUNDuntuk trueberjalan di latar belakang memang membingungkan! Nama variabel yang tepat dibiarkan sebagai latihan untuk pembaca.)

Stephen Kitt
sumber
4
itu bagus tetapi itu tidak akan bekerja di shell tanpa kontrol pekerjaan (yaitu skrip apa pun kecuali set -mdigunakan).
Mosvy
9

Anda mungkin harus menggunakan eval:

eval "sleep 5" "$BCKGRND"

evalmenyebabkan shell mengevaluasi kembali argumen yang diberikan. &Karena itu literal akan ditafsirkan sebagai &di akhir perintah dan bukan sebagai argumen untuk perintah, menempatkan perintah di latar belakang.

Kusalananda
sumber
2
Jawaban yang berisi evalharus mengandung peringatan, bahwa ini harus ditangani dengan hati-hati. Lihat misalnya jawaban ini .
Ralf
Saya tidak mengerti apa masalahnya "$BCKGRND"mengevaluasi ke argumen kosong.
Mosvy
2
@Ralf sama sekali tidak relevan dalam kasus ini . Misalnya, tidak ada yang istimewa dengan eval - Anda dapat menjalankan perintah melalui ekspansi aritmatika. Mungkin harus ada peringatan agar tidak menggunakan bash (atau shell serupa) ;-)
mosvy
1
@ Kusalananda evalakan bergabung dengan argumennya dengan spasi sebelum melakukan eval yang sebenarnya. Coba saja: eval printf "'{%s}\n'" foo "" "" "". eval foo "" "" "" ""sangat mirip dengan eval foo, tidak peduli apa IFSatau hal lainnya.
Mosvy
1
Perintah yang dievaluasi harus dalam tanda kutip ganda jika berisi karakter khusus, misalnya eval 'sleep $TIMEOUT' "$BACKGROUND". Kalau tidak, Anda bisa mendapatkan ekspansi ganda jika variabel diperluas ke variabel lain atau berisi karakter khusus. Juga, kutipan bersarang bisa menjadi rumit.
Barmar