Đặt biến môi trường biến trong bash (hoặc khác)


9

Tôi muốn tập lệnh của mình đọc một tệp chứa các cặp khóa / giá trị của các biến môi trường để đặt và sau đó đặt chúng.

Cho đến nay, tôi có điều này:

#!/bin/bash

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

Và tôi muốn đọc một tập tin như thế này:

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

Tôi chỉ cần các biến này trong phạm vi cho thời lượng của tập lệnh, vì vậy tôi không cần giải quyết vấn đề đặt biến trong tập lệnh cho phạm vi cha .

Từ thử nghiệm của tôi, tôi nghĩ vấn đề của tôi nằm ở chỗ export $key="$val", vì vậy tôi nghĩ rằng tôi chỉ cần một sự thay thế cho dòng đó.


hơi lạc đề nhưng vì tôi bị kẹt trong khi cố gắng bắt chước tập lệnh của bạn, tôi đang chia sẻ ... khi lấy khóa ... sử dụng key=${kv%%=*}tốt hơn key=${kv%=*}vì %% và ## khác với% và # cho dù phần bị xóa là phần có thể tháo rời ngắn nhất hoặc dài nhất
pulkitsinghal

Câu trả lời:


10

export $key=$valnên chỉ làm việc tốt ở bash. Tôi nghi ngờ vấn đề của bạn là ống ( |). Tất cả các lệnh trong một đường ống được thực hiện trong một lớp con. Trong ví dụ của bạn, ví dụ về bashviệc đang chạy tập lệnh của bạn sẽ tắt 2 chuỗi con: một cho cat $1và một cho while read .... Các biến được gán và xuất trong lớp con chạy vòng lặp while, và sau đó tất cả chúng được loại bỏ ngay lập tức khi lớp con thoát ra. Một cách để khắc phục điều này là không sinh ra các mạng con. Thay vì sử dụng mèo vô dụng , thay vào đó hãy thử chuyển hướng:

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

BashFAQ 24 giải thích điều này chi tiết hơn nhiều.

Một cách tiếp cận khác là chỉ lấy nguồn tệp, với xuất khẩu được ném vào:

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

Đây <( )là quá trình thay thế.

Lưu ý thêm, vì bạn đang đọc các biến môi trường từ một đối số, bạn phải đảm bảo rằng tệp đối số $1là nguồn đáng tin cậy để nó không thể làm điều xấu đối với tập lệnh của bạn.


Tôi đã sử dụng chuyển hướng trước đây; một cái gì đó phải không hoạt động khi tôi thay đổi tập lệnh để sử dụng cat. Nhưng, kịch bản với chuyển hướng hoạt động ngay bây giờ, cảm ơn!
palswim

Sử dụng vô ích của mèo coi có hại!
Scott

2

( jw013 đã đưa ra một câu trả lời đúng , nhưng tôi muốn chỉ ra một vài vấn đề nữa.)

Phía bên phải của một đường ống chạy trong lớp con riêng của nó trong bash (cũng như hầu hết các shell khác, ngoại lệ là ATT ksh và zsh). Vì vậy, bạn đang đặt các biến môi trường trong lớp con thực thi vòng lặp, nhưng không phải trong quy trình trình bao chính.

Đây là một cách khắc phục dễ dàng để thay thế việc sử dụng vô dụng catbằng cách chuyển hướng:

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

Nói chung, nếu bạn cần xử lý trước đầu vào, hãy đặt vòng lặp và phần còn lại của tập lệnh (ít nhất là phần cần các biến đã thay đổi) bên trong một khối.

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

Lưu ý rằng nói chung, while read line; do …không hoàn toàn lặp lại các dòng đầu vào. Xem tại sao được while IFS= readsử dụng thường xuyên, thay vì IFS=; while read..? cho một lời giải thích. Thành ngữ chính xác để lặp qua các dòng đầu vào là

while IFS= read -r line; do 

Ở đây, việc loại bỏ khoảng trắng hàng đầu và dấu vết không thành vấn đề vì không nên có bất kỳ đầu vào mẫu nào của bạn, nhưng bạn có thể cần -rtránh mở rộng dấu gạch chéo ngược. while read lineThực tế có thể đó là một cách chính xác để đọc đầu vào của bạn (nhưng tôi chỉ nghi ngờ nếu các giá trị biến không chứa dòng mới). Cũng có thể định dạng đầu vào của bạn không rõ ràng hoặc phức tạp hơn để phân tích cú pháp. Kiểm tra xem điều gì xảy ra nếu một trong các giá trị của biến chứa ký tự dòng mới, trích dẫn kép hoặc dấu gạch chéo ngược.

Một điểm mà bạn chắc chắn đang xáo trộn đầu vào là khi bạn trích xuất giá trị:

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

Trước tiên, bạn cần sử dụng dấu ngoặc kép xung quanh thay thế biến : echo "${kv#*=}"; mặt khác, shell thực hiện phân tách từ và tạo tên tệp (nghĩa là tạo khối) trên nó. Thứ hai, echokhông phải là một cách đáng tin cậy để in một chuỗi, bởi vì một số chuỗi bắt đầu bằng một -được coi là tùy chọn và một số shell thực hiện mở rộng dấu gạch chéo ngược trên đối số. Một cách đáng tin cậy và di động để in một chuỗi là printf %s "${kv#*=}". Ngoài ra, nếu giá trị kéo dài nhiều dòng, chương trình sed sẽ hoạt động trên từng dòng riêng biệt, điều này là sai. Điều này có thể sửa được, nhưng có một phương pháp dễ dàng hơn, đó là sử dụng các phương tiện thao tác chuỗi của shell : val=${kv#*=}; val=${val#\"}; val=${val%\"}.

Giả sử rằng đầu vào của bạn được phân tích cú pháp chính xác bằng một đơn giản read, đây là cách đơn giản hơn để phân tích cú pháp, tận dụng IFScài đặt để phân tách từng dòng:

while read name value; do
  value=${value#\"}; value=${value%\"}
  export name="$value"
done <"$1"

0

Một lựa chọn tốt khác có thể là đặt exporttệp vào và lấy nguồn:

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

và sau đó:

. "$1"

0

/ env

EXPORT=value
#NOTEXPORT=notvalue

kịch bản

#!/bin/sh

for entry in $(cat /env)
do
  if [[ ! $entry == \#* ]]
  then
    export $entry
  fi
done
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.