Truyền tất cả các biến từ tập lệnh shell này sang tập lệnh shell khác?


192

Hãy nói rằng tôi có một tập lệnh shell / bash có tên test.sh:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

Tôi test2.shtrông như thế này:

#!/bin/bash

echo ${TESTVARIABLE}

Điều này không hoạt động. Tôi không muốn truyền tất cả các biến làm tham số vì imho đây là quá mức cần thiết.

Có cách nào khác không?


Một ý tưởng là lưu các biến trong một tệp và sau đó tải vào một tập lệnh khác.
Rodrigo

@Rodrigo Tôi đã sử dụng một cách tiếp cận tương tự trước đây để "lưu" các giá trị giữa các lần chạy tập lệnh với một số thành công. Chỉ cần lưu ý một điều, là nếu nhiều phiên bản của tập lệnh chạy, mọi thứ có thể trở nên tồi tệ. Nhưng nếu bạn lưu chúng dưới dạng gán bash, bạn có thể chỉ cần nguồn tệp để nhập các biến
FatalError

Câu trả lời:


264

Về cơ bản, bạn có hai tùy chọn:

  1. Biến biến thành một biến môi trường ( export TESTVARIABLE) trước khi thực hiện tập lệnh thứ 2.
  2. Nguồn kịch bản thứ 2, nghĩa là . test2.shnó sẽ chạy trong cùng một trình bao. Điều này sẽ cho phép bạn chia sẻ các biến phức tạp hơn như mảng một cách dễ dàng, nhưng cũng có nghĩa là tập lệnh khác có thể sửa đổi các biến trong vỏ nguồn.

CẬP NHẬT:

Để sử dụng exportđể đặt biến môi trường, bạn có thể sử dụng biến hiện có:

A=10
# ...
export A

Điều này nên làm việc trong cả hai bashsh. bashcũng cho phép nó được kết hợp như vậy:

export A=10

Điều này cũng hoạt động trong của tôi sh (mà xảy ra bash, bạn có thể sử dụng echo $SHELLđể kiểm tra). Nhưng tôi không tin rằng điều đó đảm bảo sẽ hoạt động shtốt, vì vậy tốt nhất là chơi an toàn và tách chúng ra.

Bất kỳ biến nào bạn xuất theo cách này sẽ được hiển thị trong các tập lệnh bạn thực thi, ví dụ:

tro:

#!/bin/sh

MESSAGE="hello"
export MESSAGE
./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Sau đó:

$ ./a.sh
The message is: hello

Thực tế là cả hai kịch bản shell cũng chỉ là ngẫu nhiên. Các biến môi trường có thể được truyền cho bất kỳ quá trình nào bạn thực hiện, ví dụ nếu chúng ta sử dụng python thay vào đó, nó có thể trông như sau:

tro:

#!/bin/sh

MESSAGE="hello"
export MESSAGE
./b.py

b.py:

#!/usr/bin/python

import os

print 'The message is:', os.environ['MESSAGE']

Tìm nguồn cung ứng:

Thay vào đó chúng ta có thể nguồn như thế này:

tro:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Sau đó:

$ ./a.sh
The message is: hello

Điều này ít nhiều "nhập khẩu" nội dung của b.shtrực tiếp và thực hiện nó trong cùng một vỏ . Lưu ý rằng chúng tôi đã không phải xuất biến để truy cập nó. Điều này ngầm chia sẻ tất cả các biến bạn có, cũng như cho phép tập lệnh khác thêm / xóa / sửa đổi các biến trong trình bao. Tất nhiên, trong mô hình này, cả hai tập lệnh của bạn phải cùng ngôn ngữ ( shhoặc bash). Để đưa ra một ví dụ về cách chúng ta có thể truyền tin nhắn qua lại:

tro:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

echo "[A] The message is: $MESSAGE"

b.sh:

#!/bin/sh

echo "[B] The message is: $MESSAGE"

MESSAGE="goodbye"

Sau đó:

$ ./a.sh
[B] The message is: hello
[A] The message is: goodbye

Điều này hoạt động tốt như nhau trong bash. Nó cũng giúp bạn dễ dàng chia sẻ dữ liệu phức tạp hơn mà bạn không thể biểu thị dưới dạng biến môi trường (ít nhất là không có phần nặng nề của bạn), như mảng hoặc mảng kết hợp.


1
Điều gì sẽ xảy ra nếu tôi cần chuyển $ 1 cho shell phụ (vì 'sudo sh -c ...' được gọi từ tập lệnh)? Tôi có phải chuyển $ 1 vào một biến môi trường, xuất nó và sử dụng biến đó trong lệnh không?
Urhixidur

Chỉ cần thêm rằng nếu bạn cần quyền truy cập sudo, bạn có thể sử dụng sudo -E ... để bảo toàn các biến môi trường.
yucer

2
@FirthError Bạn có thể vui lòng giải thích điều kỳ diệu trong a.sh cuối cùng, nơi bạn gọi "./b.sh". Ý nghĩa của dấu chấm đầu tiên là gì? Nó hoạt động btw tuyệt vời!
Deian

bạn cũng có thể đặt các biến và giá trị cho một tệp và chia sẻ chúng theo cách đó
newshorts

@Deian, Khoảng thời gian (dấu chấm) là viết tắt của bash được xây dựng trong "nguồn"
Kirill Kost

27

Lỗi nghiêm trọng đã cho một khả năng đơn giản: nguồn kịch bản thứ hai của bạn! nếu bạn lo lắng rằng tập lệnh thứ hai này có thể thay đổi một số biến quý giá của bạn, bạn luôn có thể tìm nguồn trong tập hợp con:

( . ./test2.sh )

Các dấu ngoặc đơn sẽ làm cho nguồn xảy ra trong một lớp con, do đó, lớp vỏ cha mẹ sẽ không thấy các sửa đổi test2.shcó thể thực hiện.


Có một khả năng khác chắc chắn nên được tham khảo ở đây: sử dụng set -a.

Từ tài liệu tham khảo POSIXset :

-a: Khi tùy chọn này được bật, thuộc tính xuất sẽ được đặt cho từng biến mà việc gán được thực hiện; xem âm lượng Định nghĩa cơ sở của IEEE Std 1003.1-2001, Mục 4.21, Bài tập biến . Nếu việc gán trước một tên tiện ích trong một lệnh, thuộc tính export sẽ không tồn tại trong môi trường thực thi hiện tại sau khi tiện ích hoàn thành, ngoại trừ một trong các tiện ích tích hợp đặc biệt làm cho thuộc tính export tồn tại sau khi được xây dựng trong đã hoàn thành. Nếu nhiệm vụ không đặt trước tên tiện ích trong lệnh hoặc nếu việc gán là kết quả của hoạt động của các getopts hoặc đọc các tiện ích, thuộc tính export sẽ tồn tại cho đến khi biến không được đặt.

Từ Cẩm nang Bash :

-a: Đánh dấu các biến và hàm được sửa đổi hoặc tạo để xuất sang môi trường của các lệnh tiếp theo.

Vì vậy, trong trường hợp của bạn:

set -a
TESTVARIABLE=hellohelloheloo
# ...
# Here put all the variables that will be marked for export
# and that will be available from within test2 (and all other commands).
# If test2 modifies the variables, the modifications will never be
# seen in the present script!
set +a

./test2.sh

 # Here, even if test2 modifies TESTVARIABLE, you'll still have
 # TESTVARIABLE=hellohelloheloo

Quan sát rằng các thông số kỹ thuật chỉ xác định rằng với set -abiến được đánh dấu để xuất. Đó là:

set -a
a=b
set +a
a=c
bash -c 'echo "$a"'

sẽ lặp lại cvà không phải là một dòng trống cũng không b(đó làset +a không đánh dấu cho xuất khẩu, nó cũng không lưu lại giá trị của phép gán chỉ cho môi trường xuất). Tất nhiên đây là hành vi tự nhiên nhất.

Kết luận: sử dụng set -a/ set +acó thể ít tẻ nhạt hơn so với xuất thủ công tất cả các biến. Nó tốt hơn so với tìm nguồn cung cấp tập lệnh thứ hai, vì nó sẽ hoạt động cho bất kỳ lệnh nào, không chỉ các tập lệnh được viết bằng cùng ngôn ngữ shell.


18

Thực sự có một cách dễ dàng hơn là xuất và bỏ đặt lại hoặc tìm nguồn cung ứng lại (ít nhất là trong bash, miễn là bạn ổn với việc chuyển các biến môi trường theo cách thủ công):

hãy để a.sh

#!/bin/bash
secret="winkle my tinkle"
echo Yo, lemme tell you \"$secret\", b.sh!
Message=$secret ./b.sh

và b.sh được

#!/bin/bash
echo I heard \"$Message\", yo

Đầu ra quan sát là

[cướp @ Archie thử nghiệm] $ ./a.sh
Yo, lemme cho bạn biết "Winkle kêu leng keng của tôi", b.sh!
Tôi nghe thấy "winkle my leng keng", yo

Phép thuật nằm ở dòng cuối cùng a.sh, trong đó Message, chỉ trong khoảng thời gian của lệnh gọi ./b.sh, được đặt thành giá trị secrettừ a.sh. Về cơ bản, nó giống như các tham số / đối số được đặt tên. Tuy nhiên, nhiều hơn thế, nó thậm chí hoạt động cho các biến như$DISPLAY , mà điều khiển mà X Server một ứng dụng bắt đầu trong.

Hãy nhớ rằng, độ dài của danh sách các biến môi trường không phải là vô hạn. Trên hệ thống của tôi với một hạt nhân tương đối vanilla, xargs --show-limitscho tôi biết kích thước tối đa của bộ đệm đối số là 2094486 byte. Về mặt lý thuyết, bạn đang sử dụng các tập lệnh shell sai nếu dữ liệu của bạn lớn hơn thế (ống, bất cứ ai?)


7

Thêm vào câu trả lời của Lỗi nghiêm trọng, Có một cách nữa để chuyển các biến sang tập lệnh shell khác.

Các giải pháp được đề xuất ở trên có một số nhược điểm:

  1. using Export : Nó sẽ làm cho biến xuất hiện ngoài phạm vi của chúng không phải là một thực tiễn thiết kế tốt.
  2. using Source : Nó có thể gây ra xung đột tên hoặc ghi đè ngẫu nhiên của một biến được xác định trước trong một số tệp script shell khác có nguồn gốc một tệp khác.

Có một giải pháp đơn giản khác có sẵn cho chúng tôi sử dụng. Xem xét ví dụ được đăng bởi bạn,

kiểm tra

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh "$TESTVARIABLE"

test2.sh

#!/bin/bash

echo $1

đầu ra

hellohelloheloo

Ngoài ra, điều quan trọng cần lưu ý ""là cần thiết nếu chúng ta vượt qua chuỗi đa từ. Lấy một ví dụ nữa

master.sh

#!/bin/bash
echo in master.sh
var1="hello world"
sh slave1.sh $var1
sh slave2.sh "$var1"
echo back to master

nô lệ1.sh

#!/bin/bash
echo in slave1.sh
echo value :$1

nô lệ2.sh

#!/bin/bash
echo in slave2.sh
echo value : $1

đầu ra

in master.sh
in slave1.sh
value :"hello
in slave2.sh
value :"hello world"

Nó xảy ra vì những lý do được mô tả khéo léo trong liên kết này


6
sử dụng sourcethực sự là một điều tốt. Về lo lắng của bạn: ghi đè ngẫu nhiên một biến được xác định trước , bạn luôn có thể nguồn trong một mạng con. Điều đó hoàn toàn giải quyết vấn đề.
gniourf_gniourf

@gniourf_gniourf làm thế nào để tìm nguồn vào một subshell?
Toskan

@Toskan Điều này được đề cập trong câu trả lời của tôi : ( . ./test2.sh ). Các dấu ngoặc đơn sẽ làm cho Bash chạy nội dung của nó trong một nhánh con.
gniourf_gniourf

7

Trong Bash nếu bạn xuất biến trong một khung con, sử dụng dấu ngoặc đơn như được hiển thị, bạn sẽ tránh rò rỉ các biến đã xuất:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
(
export TESTVARIABLE    
source ./test2.sh
)

Ưu điểm ở đây là sau khi bạn chạy tập lệnh từ dòng lệnh, bạn sẽ không thấy $ TESTVARIABLE bị rò rỉ vào môi trường của mình:

$ ./test.sh
hellohelloheloo
$ echo $TESTVARIABLE
                            #empty! no leak
$

Tôi đã sử dụng nó nhưng nó dường như không hoạt động- Tôi đã viết: #! / Bin / bash cho ((i = 1; i <= 3; i ++)) làm (xuất i nguồn ./script2.sh) đã thực hiện LRI nó nói:
./script2.sh:Không có

Subshell không thực sự cần thiết vì môi trường cấp cao nhất ( $dấu nhắc dòng lệnh) sẽ không bao giờ thấy xuất $TESTVARIABLE. Xuất chỉ chuyển một bản sao của một biến cho bất kỳ quá trình con tiếp theo . Không thể truyền các biến sao lưu chuỗi cho các quy trình cha trừ khi lưu giá trị vào bộ lưu trữ và đọc bộ lưu trữ đó sau trong tập lệnh quy trình cha. Có thể chuyển sang bản sao thứ hai của tập lệnh gốc nhưng đó không phải là quy trình tương tự . Một lời giải thích tuyệt vời có thể được tìm thấy ở đây .
DocSalvager

2

Một lựa chọn khác là sử dụng eval. Điều này chỉ phù hợp nếu các chuỗi được tin cậy. Kịch bản đầu tiên có thể lặp lại các bài tập biến:

echo "VAR=myvalue"

Sau đó:

eval $(./first.sh) ./second.sh

Cách tiếp cận này được đặc biệt quan tâm khi tập lệnh thứ hai bạn muốn đặt biến môi trường không có trong bash và bạn cũng không muốn exportcác biến đó, có lẽ vì chúng nhạy cảm và bạn không muốn chúng tồn tại.


1

Một cách khác, dễ dàng hơn một chút đối với tôi là sử dụng các đường ống có tên. Các ống được đặt tên cung cấp một cách để đồng bộ hóa và gửi tin nhắn giữa các quy trình khác nhau.

A.bash:

#!/bin/bash
msg="The Message"
echo $msg > A.pipe

B.bash:

#!/bin/bash
msg=`cat ./A.pipe`
echo "message from A : $msg"

Sử dụng:

$ mkfifo A.pipe #You have to create it once
$ ./A.bash & ./B.bash # you have to run your scripts at the same time

B.bash sẽ chờ tin nhắn và ngay sau khi A.bash gửi tin nhắn, B.bash sẽ tiếp tục công việc của mình.

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.