Bagaimana cara menetapkan variabel lingkungan pada baris perintah dan membuatnya muncul dalam perintah?

152

Jika saya lari

export TEST=foo
echo $TEST

Ini menghasilkan foo.

Jika saya lari

TEST=foo echo $TEST

Itu tidak. Bagaimana saya bisa mendapatkan fungsionalitas ini tanpa menggunakan ekspor atau skrip?

ashleysmithgpu
sumber
Hati-hati, ada lebih banyak cerita ini daripada yang muncul pada awalnya. Saya mengundang Anda untuk memeriksa jawaban saya.
jasonleonhard

Jawaban:

180

Ini karena shell memperluas variabel dalam baris perintah sebelum benar-benar menjalankan perintah dan pada saat itu variabel tidak ada. Jika Anda menggunakan

TEST=foo; echo $TEST

ini akan bekerja.

exportakan membuat variabel muncul di lingkungan perintah yang dieksekusi selanjutnya (untuk tentang bagaimana ini bekerja di bash lihat help export). Jika Anda hanya perlu variabel untuk muncul di lingkungan satu perintah, gunakan apa yang Anda coba, yaitu:

TEST=foo your-application
peterph
sumber
1
Bagaimana dengan / usr / bin / env? itu juga tidak berhasil, tahukah Anda mengapa?
Benubird
5
Alasan yang sama - shell mengekspansi $TESTsebelum baris perintah dieksekusi. Setelah echoberjalan (perhatikan juga bahwa echobiasanya akan menerjemahkan ke perintah built-in shell dan tidak ke /bin/echo) ia melihat set variabel di lingkungannya. Namun, echo $TESTtidak memberi tahu echountuk mengeluarkan isi variabel TESTdari lingkungannya. Ia memberi tahu shell untuk menjalankan echoargumen menjadi apa pun yang saat ini ada dalam variabel yang disebut TEST- dan itu adalah dua hal yang sangat berbeda.
peterph
@peterph Jika shell memperluas variabel di baris perintah sebelum benar-benar menjalankan perintah lalu mengapa ia tidak memperluasnya var=value sh -c 'echo "$var"'?
jam
Seperti yang saya jelaskan kepada Anda di sini : itu karena variabel diperluas di dalam tanda kutip ganda (misalnya, "… $var …") tetapi tidak di dalam tanda kutip tunggal (misalnya, '… $var …'). Karena echo "$var"ada di dalam tanda kutip tunggal, seluruh string akan diteruskan ke sh -cshell ( ) baru tanpa ditafsirkan oleh shell interaktif luar. … (Lanjutan)
G-Man
(Lanjutkan) ... Seperti igal katakan kemarin , ada dua putaran penguraian - meskipun saya percaya bahwa igal salah membaca pertanyaan Anda. Tidak ada dua putaran parsing selain parsing yang dilakukan oleh shell induk. Ada dua putaran parsing - satu dilakukan oleh kulit luar, interaktif (induk), dan satu oleh sh -ckulit anak baru ( ).
G-Man
39

Saya menduga Anda ingin memiliki variabel shell untuk memiliki cakupan terbatas, daripada variabel lingkungan. Variabel lingkungan adalah daftar string yang diteruskan ke perintah ketika dieksekusi .

Di

var=value echo whatever

Anda meneruskan var=valuestring ke lingkungan yang menerima gema. Namun, echotidak melakukan apa-apa dengan daftar lingkungannya dan tetap di sebagian besar shell, echodibangun dan karenanya tidak dieksekusi .

Jika Anda telah menulis

var=value sh -c 'echo "$var"'

Itu akan menjadi masalah lain. Di sini, kita meneruskan var=valueke shperintah, dan shkebetulan menggunakan lingkungannya. Shell mengonversi masing-masing variabel yang mereka terima dari lingkungannya ke variabel shell, sehingga varvariabel lingkungan yang shditerima akan dikonversi ke $varvariabel, dan ketika itu diperluas di echobaris perintah itu, itu akan menjadi echo value. Karena lingkungan secara default diwarisi, echojuga akan menerima var=valuedi lingkungannya (atau akan jika itu dijalankan), tetapi sekali lagi, echotidak peduli dengan lingkungan.

Sekarang, jika seperti yang saya duga, yang Anda inginkan adalah membatasi ruang lingkup variabel shell, ada beberapa pendekatan yang mungkin.

Portably (Bourne dan POSIX):

(var=value; echo "1: $var"); echo "2: $var"

The (...) di atas memulai sub-shell (proses shell baru di sebagian besar shell), jadi variabel apa pun yang dideklarasikan hanya akan mempengaruhi sub-shell itu, jadi saya berharap kode di atas menghasilkan output "1: value" dan "2:" atau "2: apa pun-var-sudah-ditetapkan-sebelumnya".

Dengan sebagian besar shell mirip Bourne, Anda dapat menggunakan fungsi dan builtin "lokal":

f() {
  local var
  var=value
  echo "1: $var"
}
f
echo "2: $var"

Dengan zsh, Anda dapat menggunakan fungsi sebaris:

(){ local var=value; echo "1: $var"; }; echo "2: $var"

atau:

function { local var=value; echo "1: $var"; }; echo "2: $var"

Dengan bash dan zsh (tetapi bukan ash, pdksh atau AT&T ksh), trik ini juga berfungsi:

var=value eval 'echo "1: $var"'; echo "2: $var"

Varian yang bekerja dalam beberapa kerang lebih ( dash, mksh, yash) tapi tidak zsh(kecuali dalam sh/ kshemulasi):

var=value command eval 'echo "1: $var"'; echo "2: $var"

(menggunakan commanddi depan builtin khusus (di sini eval) di shell POSIX menghapus keistimewaan mereka (di sini bahwa penugasan variabel dari mereka tetap berlaku setelah mereka kembali))

Stéphane Chazelas
sumber
Untuk tujuan saya, ini adalah solusi yang tepat. Saya tidak ingin variabel 'var' mencemari lingkungan seperti halnya dalam jawaban yang diterima.
kronenpj
6

Anda melakukannya dengan benar, tetapi sintaks bash adalah mudah untuk salah menafsirkan: Anda bisa berpikir bahwa echo $TESTmenyebabkan echountuk mengambil TESTenv var kemudian mencetaknya, tidak. Jadi diberikan

export TEST=123

kemudian

TEST=456 echo $TEST

melibatkan urutan berikut:

  1. Shell mem-parsing seluruh baris perintah dan mengeksekusi semua penggantian variabel, sehingga baris perintah menjadi

    TEST=456 echo 123
  2. Itu menciptakan vars temp ditetapkan sebelum perintah, sehingga menyimpan nilai saat ini TESTdan menimpanya dengan 456; baris perintah sekarang

    echo 123
  3. Ini mengeksekusi perintah yang tersisa, yang dalam hal ini mencetak 123 ke stdout (jadi perintah shell yang tetap bahkan tidak menggunakan nilai temp dari TEST)

  4. Ini mengembalikan nilai TEST

Gunakan printenv sebagai gantinya, karena tidak melibatkan substitusi variabel:

>> export TEST=123
>> printenv TEST
123
>> TEST=456 printenv TEST
456
>> printenv TEST && TEST=456 printenv TEST && TEST=789 printenv TEST && printenv TEST
123
456
789
123
>>
Oliver
sumber
+1 Saya merasa printenvbermanfaat untuk menguji / membuktikan konsep (berperilaku seperti naskah, berlawanan dengan echo)
Daryn
5

Anda bisa membuatnya bekerja dengan menggunakan:

TEST=foo && echo $TEST
pradeepchhetri
sumber
6
Dalam hal ini, TEST=foodijalankan sebagai pernyataan terpisah - tidak hanya diatur dalam konteks echo.
codeforester