2008-08-05 45 views
16

Bất kỳ ai có thể giải thích hành vi này? Chạy:Số lần chuyển hướng đầu vào của tập lệnh shell

#!/bin/sh 
echo "hello world" | read var1 var2 
echo $var1 
echo $var2 

kết quả trong không có gì là ouput, trong khi:

#!/bin/sh 
echo "hello world" > test.file 
read var1 var2 < test.file 
echo $var1 
echo $var2 

sản xuất sản lượng dự kiến:

hello 
world 

nên không phải là ống làm trong một bước những gì mà chuyển hướng đến test.file đã làm trong ví dụ thứ hai? Tôi đã thử cùng một mã với cả dấu gạch ngang và bash và có cùng hành vi từ cả hai.

Trả lời

8

Một phần vừa thêm vào bashlastpipe tùy chọn, cho phép các lệnh cuối cùng trong một đường ống chạy trong vỏ hiện tại, không phải là một subshell, khi kiểm soát công việc bị hủy kích hoạt.

#!/bin/bash 
set +m  # Deactiveate job control 
shopt -s lastpipe 
echo "hello world" | read var1 var2 
echo $var1 
echo $var2 

sẽ thực sự ra

hello 
world 
3

Đó là vì phiên bản đường ống đang tạo một vỏ con, đọc biến đó vào không gian cục bộ của nó, sau đó sẽ bị phá hủy khi thoát khỏi vỏ bọc con.

Execute lệnh này

$ echo $$;cat | read a 
10637 

và sử dụng pstree -p để xem xét các tiến trình đang chạy, bạn sẽ thấy một vỏ thêm treo tắt của vỏ chính của bạn.

|      |-bash(10637)-+-bash(10786) 
    |      |    `-cat(10785) 
5

Được rồi, tôi đã tìm ra!

Đây là một lỗi khó nắm bắt, nhưng kết quả từ cách đường ống được xử lý bởi vỏ. Mỗi phần tử của một đường ống chạy trong một quá trình riêng biệt. Khi lệnh đọc đặt var1 và var2, đặt chúng là subshell riêng của nó, không phải là shell cha. Vì vậy, khi các subshell thoát, các giá trị của var1 và var2 bị mất. Tuy nhiên, bạn có thể thử thực hiện

var1=$(echo "Hello") 
echo var1 

trả về câu trả lời mong đợi. Rất tiếc, điều này chỉ hoạt động đối với các biến đơn lẻ, bạn không thể đặt nhiều biến tại một thời điểm. Để đặt nhiều biến tại một thời điểm bạn phải hoặc là đọc vào một biến và chop nó ra thành nhiều biến hoặc sử dụng một cái gì đó như thế này:

set -- $(echo "Hello World") 
var1="$1" var2="$2" 
echo $var1 
echo $var2 

Trong khi tôi thừa nhận nó không phải là thanh lịch như sử dụng một đường ống, nó hoạt động . Tất nhiên bạn nên ghi nhớ rằng đọc được có nghĩa là để đọc từ các tập tin vào các biến, do đó, làm cho nó đọc từ đầu vào tiêu chuẩn nên có một chút khó khăn hơn.

+2

Điều này phụ thuộc vào sự lựa chọn của vỏ. Ksh93 được thiết kế để hạn chế chi phí quá trình.Nó cũng chạy phần tử cuối cùng của một đường ống * bên trong * quy trình vỏ gọi, do đó bảo toàn trạng thái. –

+1

Bash 4.2 giới thiệu một tùy chọn để làm điều tương tự. Tắt điều khiển công việc ('set + m') và đặt tùy chọn' lastpipe' ('shopt -s lastpipe'). – chepner

+0

Tôi sẽ xem xét điều đó. Cảm ơn! –

9

Điều này đã được trả lời đúng, nhưng giải pháp chưa được nêu rõ. Sử dụng ksh, không bash. Hãy so sánh:

$ echo 'echo "hello world" | read var1 var2 
echo $var1 
echo $var2' | bash -s 

Để:

$ echo 'echo "hello world" | read var1 var2 
echo $var1 
echo $var2' | ksh -s 
hello 
world 

ksh là một vỏ lập trình vượt trội vì niceties chút như thế này. (bash là vỏ tương tác tốt hơn, theo ý kiến ​​của tôi.)

3

Bài đăng đã được trả lời đúng, nhưng tôi muốn cung cấp một lớp lót thay thế có lẽ có thể sử dụng.

Để được chuyển nhượng không gian giá trị tách ra từ echo (hoặc thiết bị xuất chuẩn cho rằng vấn đề) để bao các biến, bạn có thể xem xét sử dụng các mảng vỏ:

$ var=($(echo 'hello world')) 
$ echo ${var[0]} 
hello 
$ echo ${var[1]} 
world 

Trong ví dụ var đây là một mảng và các nội dung có thể được truy cập bằng cấu trúc $ {var [index]}, trong đó chỉ mục là chỉ mục mảng (bắt đầu bằng 0).

Bằng cách đó, bạn có thể có nhiều tham số như bạn muốn được chỉ định cho chỉ mục mảng có liên quan.

+2

Giải pháp tốt. Nó hoạt động tốt trong bash, nhưng nó không hoạt động trong trình bao. –

8
read var1 var2 < <(echo "hello world") 
3

Hãy thử:

echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2) 

Vấn đề, như nhiều người đã nói, đó là var1 và var2 được tạo ra trong một môi trường subshell đó bị phá hủy khi mà thoát subshell. Ở trên tránh phá hủy subshell cho đến khi kết quả đã được echo'd. Một giải pháp khác là:

result=`echo "hello world"` 
read var1 var2 <<EOF 
$result 
EOF 
echo $var1 
echo $var2 
10
#!/bin/sh 
echo "hello world" | read var1 var2 
echo $var1 
echo $var2 

sản xuất không có đầu ra vì đường ống chạy mỗi thành phần của họ bên trong một subshell. Subshells kế thừa các bản sao các biến của trình bao của cha mẹ, thay vì chia sẻ chúng. Hãy thử điều này:

#!/bin/sh 
foo="contents of shell variable foo" 
echo $foo 
(
    echo $foo 
    foo="foo contents modified" 
    echo $foo 
) 
echo $foo 

Dấu ngoặc xác định vùng mã được chạy trong một vỏ con và $ foo giữ lại giá trị ban đầu sau khi được sửa đổi bên trong chúng.

Bây giờ thử điều này:

#!/bin/sh 
foo="contents of shell variable foo" 
echo $foo 
{ 
    echo $foo 
    foo="foo contents modified" 
    echo $foo 
} 
echo $foo 

Các niềng răng là thuần túy cho nhóm, không subshell được tạo ra, và $ foo biến đổi bên trong dấu ngoặc là như nhau $ foo biến đổi bên ngoài chúng.

Bây giờ thử điều này:

#!/bin/sh 
echo "hello world" | { 
    read var1 var2 
    echo $var1 
    echo $var2 
} 
echo $var1 
echo $var2 

Bên trong dấu ngoặc, đọc BUILTIN tạo $ var1 và $ var2 đúng và bạn có thể thấy rằng họ nhận được lặp lại. Bên ngoài niềng răng, chúng không tồn tại nữa. Tất cả các mã trong niềng răng đã được chạy trong một subshell bởi vì nó là một thành phần của một đường ống.

Bạn có thể đặt số lượng mã tùy ý giữa các dấu ngoặc, vì vậy bạn có thể sử dụng công trình xây dựng từng khối này bất cứ khi nào bạn cần chạy một khối tập lệnh shell phân tích đầu ra của một thứ khác.

+1

+1 cho các ví dụ rõ ràng - mã đẹp, dễ đọc –

4

Đưa tôi về vấn đề này (sử dụng Bash):

read var1 var2 <<< "hello world" 
echo $var1 $var2 
Các vấn đề liên quan