Batalkan variabel lingkungan untuk satu perintah

24

Saya dapat menjalankan ENV_VAR=value commanduntuk menjalankan commanddengan nilai spesifik untuk ENV_VAR. Apa yang setara dengan tidak disetel ENV_VARuntuk command?

Alexey Romanov
sumber
7
Selain: commandadalah pilihan malang placeholder, karena ada sebenarnya adalah built-in perintah dengan nama itu. mycommandatau somecommandatau mungkin itu adalah kebiasaan yang lebih baik untuk masuk.
Charles Duffy
@CharlesDuffy, biasanya saya gunakan cmduntuk itu.
Stéphane Chazelas

Jawaban:

35

Selain dengan -iopsi, yang menghapus seluruh lingkungan, POSIX envtidak menyediakan cara untuk menghapus variabel.

Namun, dengan beberapa envimplementasi (termasuk busyboxsetidaknya GNU, 'dan FreeBSD), Anda dapat melakukan:

env -u ENV_VAR command

Yang akan bekerja untuk menghapus setiap instance dari ENV_VARvariabel dari lingkungan (perlu dicatat bahwa itu tidak bekerja untuk variabel lingkungan dengan nama kosong ( env -u ''baik memberikan kesalahan atau tidak efektif tergantung pada implementasi meskipun semua menerima env '=value', mungkin batasan dikeluarkan oleh unsetenv()fungsi C yang diperlukan POSIX untuk mengembalikan kesalahan untuk string kosong, sementara tidak ada batasan untuk putenv())).

Mudah dibawa (dalam kulit POSIX), Anda dapat melakukan:

(unset -v ENV_VAR; exec command)

(perhatikan bahwa dengan beberapa shell, gunakan execcan change yang commanddijalankan: jalankan yang ada di filesystem alih-alih fungsi atau builtin misalnya (dan akan memotong aliasekspansi dengan jelas), seperti di envatas. Anda ingin menghilangkannya dalam kasus tersebut) .

Tapi itu tidak akan berfungsi untuk variabel lingkungan yang memiliki nama yang tidak dapat dipetakan ke variabel shell (perhatikan bahwa beberapa shell seperti mkshakan menghapus variabel-variabel dari lingkungan saat startup), atau variabel yang telah ditandai hanya-baca.

-vadalah untuk shell Bourne dan bashyang unsettanpa itu -vdapat membatalkan ENV_VAR fungsi jika tidak ada variabel dengan nama itu. Sebagian besar shell lain tidak akan membatalkan fungsi kecuali Anda melewati -fopsi. (tidak mungkin membuat perbedaan dalam praktik).

(Waspadalah juga terhadap bug / misfeature dari bash/ mksh/ yashyang unset, dalam keadaan tertentu mungkin tidak menghapus variabel, tetapi mengungkapkan variabel dalam lingkup luar )

Jika perltersedia, Anda dapat melakukan:

perl -e 'delete $ENV{shift@ARGV}; exec @ARGV or die$!' ENV_VAR command

Yang akan bekerja bahkan untuk variabel lingkungan dengan nama kosong.

Sekarang semua itu tidak akan berfungsi jika Anda ingin mengubah variabel lingkungan untuk builtin atau fungsi dan tidak ingin mereka berjalan dalam subkulit, seperti di bash:

env -u LANG printf -v var %.3f 1.2 # would run /usr/bin/printf instead
(unset -v LANG; printf -v var %.3f 1.2) # changes to $var lost afterwards

(di sini unsetting LANG sebagai pendekatan yang salah dalam memastikan .digunakan dan dipahami sebagai pemisah desimal. Akan lebih baik digunakan LC_ALL=C printf...untuk itu)

Dengan beberapa shell, Anda bisa membuat ruang lingkup lokal untuk variabel menggunakan fungsi:

without() {
  local "$1" # $1 variable declared as initially unset in bash¹
  shift
  "$@"
}
without LANG printf -v var %.3f 1.2

Dengan zsh, Anda juga dapat menggunakan fungsi anonim:

(){local ENV_VAR; command}

Pendekatan itu tidak akan bekerja di beberapa shell (seperti yang didasarkan pada shell Almquist), yang localtidak menyatakan variabel sebagai awalnya tidak disetel (tetapi mewarisi nilai dan atribut). Dalam hal itu, Anda dapat melakukannya local ENV_VAR; unset ENV_VAR, tetapi jangan lakukan itu di dalam mkshatau yash( typesetalih-alih localuntuk yang itu) karena itu tidak akan berhasil, unsetitu hanya akan membatalkan local.

¹ Berhati-hatilah juga bahwa dalam bash, lokal ENV_VAR(meskipun tidak disetel) akan mempertahankan atribut ekspor . Jadi jika commandfungsi yang memberikan nilai ENV_VAR, variabel akan tersedia di lingkungan perintah yang disebut sesudahnya. unset ENV_VARakan menghapus atribut itu. Atau Anda dapat menggunakan local +x ENV_VARyang juga akan memastikan bahwa batu tulis bersih (kecuali variabel itu telah dinyatakan sebagai read-only, tetapi kemudian tidak ada yang dapat Anda lakukan tentang hal itu bash).

Stéphane Chazelas
sumber
1
Ini pertanyaan terpisah, tapi apa sih variabel lingkungan dengan nama kosong dan bagaimana Anda akan menggunakannya - bahkan untuk semacam eksploitasi? Tidakkah Anda harus menulis sebuah program yang membaca lingkungan dan secara khusus mencari sesuatu seperti ini?
Joe
@ Jo, coba misalnya env '=foo' python -c 'import os; print os.environ[""]'. Saya tidak berharap banyak orang ingin mengatur variabel seperti itu.
Stéphane Chazelas
9

Tidak ada padanan langsung sejauh yang saya ketahui; Anda dapat menggunakan subkulit:

(unset ENV_VAR; exec command)
Stephen Kitt
sumber
(unset ENV_VAR; exec somecommand)jika Anda ingin menjadi setara dengan yang asli dalam efisiensi (dengan mengkonsumsi subkulit). Seperti ini, ini menambahkan garpu tambahan.
Charles Duffy
1
@ Charles memang, meskipun beberapa shell melakukannya secara otomatis karena commandini adalah perintah terakhir dalam doa subkulit.
Stephen Kitt
+1 karena cara ini mudah diketik untuk penggunaan interaktif. Saya menggunakan subkulit untuk perubahan satu kali ke lingkungan shell untuk satu-liner alih-alih mengingatnya env -u.
Peter Cordes
0

Anda dapat gunakan ENV_VAR= command

Ini sebenarnya tidak menghapus variabel, tetapi mungkin berguna dalam banyak kasus (skrip shell biasanya hanya digunakan test -nuntuk memeriksa apakah suatu variabel diatur):

$ export ENV_VAR=foo
$ mycommand
ENV_VAR: foo
$ ENV_VAR=bar mycommand
ENV_VAR: bar
$ ENV_VAR= mycommand
ENV_VAR: 
GnP
sumber