Set Variabel Lingkungan Variabel di bash (atau lainnya)

9

Saya ingin skrip saya membaca file yang berisi pasangan kunci / nilai variabel lingkungan untuk diatur, lalu mengaturnya.

Sejauh ini, saya punya ini:

#!/bin/bash

cat $1 | while read kv
do
    key=${kv%=*}
    val=`echo ${kv#*=} | sed 's/^"\|"$//g'`
    export $key="$val"
done

Dan saya ingin membaca file seperti ini:

XAUTHLOCALHOSTNAME="localhost"
DISPLAY=":0"
XAUTHORITY="/tmp/some-XAuthority"

Saya hanya perlu variabel ini dalam cakupan selama durasi skrip, jadi saya tidak perlu menyelesaikan masalah pengaturan variabel dalam skrip untuk cakupan induk .

Dari pengujian saya, saya pikir masalahnya terletak pada export $key="$val", jadi saya pikir saya hanya perlu pengganti untuk jalur itu.

palswim
sumber
sedikit keluar dari topik, tetapi karena saya macet ketika mencoba meniru skrip Anda, saya membagikan ... ketika mengambil kunci ... menggunakan key=${kv%%=*}lebih baik daripada key=${kv%=*}karena %% dan ## berbeda dari% dan # untuk apakah bagian yang dihapus adalah bagian yang dapat dilepas terpendek atau terpanjang
pulkitsinghal

Jawaban:

10

export $key=$valharus bekerja dengan baik bash. Saya menduga masalah Anda adalah pipanya ( |). Semua perintah dalam pipa dieksekusi dalam subkulit. Dalam contoh Anda, contoh bashyang menjalankan skrip Anda akan memotong 2 subkulit: satu untuk cat $1dan lain untuk while read .... Variabel ditugaskan dan diekspor dalam subkulit yang menjalankan loop sementara, dan kemudian semuanya segera dibuang ketika subkulit keluar. Salah satu cara untuk memperbaikinya adalah dengan tidak menelurkan subkulit sama sekali. Alih-alih menggunakan kucing yang tidak berguna , coba pengalihan saja:

while read ...; do
   ...
done < "$1"

BashFAQ 24 menjelaskan ini dengan lebih rinci.

Pendekatan alternatif adalah hanya sumber file, dengan ekspor dilemparkan ke:

. <(sed '/^export/!s/^/export /' "$1")

The <( )adalah proses substitusi.

Sebagai catatan peringatan, karena Anda membaca variabel lingkungan dari argumen, Anda harus memastikan bahwa file argumen $1adalah sumber tepercaya sehingga tidak dapat melakukan hal-hal buruk pada skrip Anda.

jw013
sumber
Saya menggunakan pengalihan sebelumnya; sesuatu yang lain pasti tidak berfungsi ketika saya mengubah script untuk digunakan cat. Tapi, skrip dengan pengalihan berfungsi sekarang, terima kasih!
palswim
Penggunaan kucing yang tidak berguna dianggap berbahaya!
Scott
2

( jw013 telah memberikan jawaban yang benar , tetapi saya ingin menunjukkan beberapa masalah lagi.)

Sisi kanan pipa berjalan di subkulitnya sendiri di bash (dan juga sebagian besar shell lainnya, pengecualiannya adalah ATT ksh dan zsh). Jadi Anda mengatur variabel lingkungan di subkulit yang mengeksekusi loop, tetapi tidak dalam proses shell utama.

Di sini ada perbaikan yang mudah yaitu mengganti penggunaan yang tidak berguna catdengan pengalihan:

while read …; do …; done <"$1"

Secara umum, jika Anda perlu memproses input, letakkan loop dan sisa skrip (setidaknya bagian yang membutuhkan variabel yang diubah) di dalam blok.

grep '^foo_' "$1" | {
  while read …; do …; done
  do_something
}

Perhatikan bahwa secara umum, while read line; do …tidak cukup iterate pada jalur input. Lihat Mengapa while IFS= readsering digunakan, bukan IFS=; while read..? untuk penjelasan. Ungkapan yang benar untuk beralih pada jalur input adalah

while IFS= read -r line; do 

Di sini, penghapusan spasi putih depan dan akhir tidak menjadi masalah karena seharusnya tidak ada input sampel yang diberikan, tetapi Anda mungkin perlu -rmenghindari ekspansi backslash. Mungkin saja while read linesebenarnya cara yang benar untuk membaca input Anda (tapi saya curiga hanya jika nilai variabel tidak mengandung baris baru). Mungkin juga format input Anda ambigu atau lebih rumit untuk diuraikan. Periksa apa yang terjadi jika salah satu nilai variabel berisi karakter baris baru, kutipan ganda atau garis miring terbalik.

Satu titik di mana Anda benar-benar mengacaukan input adalah ketika Anda mengekstrak nilainya:

val=`echo ${kv#*=} | sed 's/^"\|"$//g'`

Pertama, Anda perlu menggunakan tanda kutip ganda sekitar substitusi variabel: echo "${kv#*=}"; jika tidak, shell melakukan pemisahan kata dan nama file generasi (yaitu globbing) di atasnya. Kedua, echobukan cara yang dapat diandalkan untuk mencetak string, karena beberapa string yang dimulai dengan a -diperlakukan sebagai opsi, dan beberapa shell melakukan ekspansi backslash pada argumen. Cara yang andal dan portabel untuk mencetak string adalah printf %s "${kv#*=}". Juga, jika nilainya mencakup beberapa baris, program sed akan bertindak pada masing-masing secara terpisah, yang mana salah. Ini diperbaiki, tapi ada metode yang lebih mudah, yaitu dengan menggunakan fasilitas manipulasi string shell: val=${kv#*=}; val=${val#\"}; val=${val%\"}.

Dengan asumsi bahwa input Anda diurai dengan benar dengan sebuah dataran read, berikut ini adalah cara yang lebih sederhana untuk menguraikannya, mengambil keuntungan dari IFSpengaturan untuk membagi setiap baris:

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"
Gilles 'SANGAT berhenti menjadi jahat'
sumber
0

Pilihan lain yang bagus adalah dengan meletakkannya exportdi file dan sumbernya:

export XAUTHLOCALHOSTNAME="localhost"
export DISPLAY=":0"
export XAUTHORITY="/tmp/some-XAuthority"

lalu:

. "$1"
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
sumber
0

/ env

EXPORT=value
#NOTEXPORT=notvalue

naskah

#!/bin/sh

for entry in $(cat /env)
do
  if [[ ! $entry == \#* ]]
  then
    export $entry
  fi
done
Łukasz Kurowski
sumber