trong bash, đọc sau khi ống không đặt giá trị


22

Chỉnh sửa: tiêu đề ban đầu là "đọc thất bại trong bash"

Với ksh tôi đang sử dụng đọc như một cách thuận tiện để phân tách các giá trị:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 
2 1
$

Nhưng nó thất bại trong bash:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 

$

Tôi đã không tìm thấy một lý do trong trang người đàn ông tại sao nó thất bại, ý tưởng nào?


2
Điều này được thảo luận (hơi khó hiểu) trong trang 024 của Câu hỏi thường gặp về Bash của Greg .
Scott

Câu trả lời:


27

bash chạy phía bên phải của một đường ống trong ngữ cảnh của lớp con , do đó, các thay đổi đối với các biến (đó là những gì readkhông được bảo tồn) - chúng sẽ chết khi lớp con thực hiện, ở cuối lệnh.

Thay vào đó, bạn có thể sử dụng thay thế quá trình :

$ read a b dump < <(echo 1 2 3 4 5)
$ echo $b $a
2 1

Trong trường hợp này, readđang chạy trong lớp vỏ chính của chúng ta và lệnh tạo đầu ra của chúng ta chạy trong lớp con. Các <(...)cú pháp tạo ra một subshell và kết nối đầu ra của mình cho một đường ống, mà chúng tôi chuyển hướng vào đầu vào của readvới bình thường <hoạt động . Bởi vì readchạy trong shell chính của chúng tôi, các biến được đặt chính xác.

Như đã chỉ ra trong một nhận xét, nếu mục tiêu của bạn theo nghĩa đen là chia một chuỗi thành các biến bằng cách nào đó, bạn có thể sử dụng chuỗi ở đây :

read a b dump <<<"1 2 3 4 5"

Tôi cho rằng có nhiều thứ hơn thế, nhưng đây là một lựa chọn tốt hơn nếu không có.


3
Hoặc thậm chí read a b dump <<< '1 2 3 4 5'.
choroba

Cảm ơn tất cả, btw tôi lưu ý rằng mksh (trên cygwin) đang làm tương tự như bash.
Emmanuel

@Michael Homer Giải thích tốt! Tôi tìm thấy một ví dụ khác có thể giải thích rằng mọi lệnh trong đường ống đều chạy trong lớp con riêng : cat /etc/passwd | (read -r line ; echo $line). Nhưng tiếp theo echocủa $linemà không có trong đường ống đặt gì trên màn hình, bởi vì giá trị đã tồn tại chỉ giữa dấu ngoặc đơn (subshell). Hy vọng, nó giúp được ai đó.
Yurij Goncharuk

17

Đây không phải là một bashlỗi vì POSIXcho phép cả hai bashkshhành vi, dẫn đến sự khác biệt đáng tiếc mà bạn đang quan sát.

http://pub.opengroup.org/onlinepub/9699919799/utilities/V3_chap02.html#tag_18_12

Ngoài ra, mỗi lệnh của một đường ống đa lệnh nằm trong môi trường mạng con; như một phần mở rộng, tuy nhiên, bất kỳ hoặc tất cả các lệnh trong một đường ống có thể được thực thi trong môi trường hiện tại. Tất cả các lệnh khác sẽ được thực hiện trong môi trường shell hiện tại.

Tuy nhiên, với bash 4.2và mới hơn, bạn có thể đặt lastpipetùy chọn trong các tập lệnh không tương tác để có được kết quả mong đợi, ví dụ:

#!/bin/bash

echo 1 2 3 4 5 | read a b dump
echo before: $b $a 
shopt -s lastpipe
echo 1 2 3 4 5 | read a b dump
echo after: $b $a 

Đầu ra:

before:
after: 2 1

1
+1 cảm ơn bạn về thông tin "Lastpipe". xin lỗi vì sự chậm trễ
Emmanuel

1
vấn đề với lastpipelà nó không hoạt động trong các shell khác (ví dụ dash). về cơ bản không có cách nào để thực hiện việc di động này khi chạy mọi thứ trong mạng con đó, hãy xem stackoverflow.com/questions/36268479/
trộm

1
@anarcat Điều này đúng nhưng câu hỏi được hỏi ở đây là về bash.
jlliagre

@anarcat: Điều này có thể thay đổi trong tương lai vì muốn thay đổi POSIX vì một lý do khác (Trạng thái PIPE) xem tại đây: unix.stackexchange.com/questions/476834/. Thay đổi các shell khác không phải là chuyện nhỏ, tôi phải mất vài tháng để viết lại trình phân tích cú pháp và trình thông dịch trên Bourne Shell (bosh) để thực hiện hành vi nhanh hơn của ksh hiện đại.
schily
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.