Không thể đặt ống trong bản đồ của Bash, nhưng tại sao?


13

Tôi chỉ muốn nhận tất cả các tệp trong một thư mục nhất định vào một mảng bash (giả sử rằng không có tệp nào có dòng mới trong tên):

Vì thế:

myarr=()
find . -maxdepth 1  -name "mysqldump*" | mapfile -t myarr; echo "${myarr[@]}"

Kết quả trống rỗng!

Nếu tôi thực hiện cách sử dụng tệp vòng, tạm thời hoặc theo cách khác:

myarr=()
find . -maxdepth 1  -name "mysqldump*" > X
mapfile -t myarray < X
echo "${myarray[@]}"

Kết quả!

Nhưng tại sao không mapfileđọc đúng từ một đường ống?



Câu trả lời tuyệt vời xung quanh, cảm ơn tất cả mọi người. Thật thú vị khi chiến lược thực hiện của đường ống (mỗi phần chạy trong quy trình spearate) rò rỉ "lên trên" và sửa đổi ý nghĩa rõ ràng của mã, về cơ bản âm thầm đặt "cục bộ" trước mỗi biến xuất hiện trong đường ống. Trong một ngôn ngữ là một cái gì đó khác với keo điên cho các chương trình khác, đó sẽ là lỗi, hy vọng.
David Tonhofer

2
Nếu bạn cung cấp mã cho shellcheck , bạn sẽ nhận được cảnh báo: SC2030 : "Sửa đổi var là cục bộ (thành subshell gây ra bởi đường ống)"SC2031 : "var đã được sửa đổi trong một subshell. Thay đổi đó có thể bị mất." . Thông minh.
David Tonhofer

Tại sao sử dụng findmapfileở đây ở tất cả và không chỉ đơn giản myarr=(mysqldump*)? Điều này thậm chí sẽ làm việc với tên tập tin với không gian và dòng mới.
BlackJack

1
Chỉ cần lưu ý rằng người ta phải bật nullglobtùy chọn ( shopt -s nullglob) myarr=(mysqldump*)để không kết thúc với mảng ('mysqldump*')trong trường hợp không có tệp nào khớp.
David Tonhofer

Câu trả lời:


25

Từ man 1 bash:

Mỗi lệnh trong một đường ống được thực thi như một quy trình riêng biệt (nghĩa là trong một lớp con).

Các subshells như vậy thừa hưởng các biến từ shell chính nhưng chúng độc lập. Điều này có nghĩa là mapfiletrong lệnh ban đầu của bạn tự hoạt động myarr. Sau đó echo(ở bên ngoài đường ống) in trống myarr(đó là vỏ chính myarr).

Lệnh này hoạt động khác nhau:

find . -maxdepth 1 -name "mysqldump*" | { mapfile -t myarr; echo "${myarr[@]}"; }

Trong trường hợp này mapfileechohoạt động trên cùng myarr(không phải là vỏ chính myarr).

Để thay đổi vỏ chính, myarrbạn phải chạy mapfiletrong vỏ chính. Thí dụ:

myarr=()
mapfile -t myarr < <(find . -maxdepth 1 -name "mysqldump*")
echo "${myarr[@]}"

Đã thêm liên kết đến "nhóm thay thế" như được đưa ra trong phản hồi của Attie, trong trường hợp khách truy cập có thời điểm TL; DR.
David Tonhofer

11

Bash chạy các lệnh của một đường ống dẫn trong môi trường lớp con, do đó, bất kỳ phép gán biến nào, v.v. diễn ra trong nó không thể nhìn thấy với phần còn lại của vỏ.

Dash (Debian /bin/sh) cũng như busybox shlà tương tự nhau, trong khi zsh và ksh chạy phần cuối cùng trong shell chính. Trong Bash, bạn có thể sử dụng shopt -s lastpipeđể làm tương tự, nhưng nó chỉ hoạt động khi điều khiển công việc bị vô hiệu hóa, do đó, không phải trong shell tương tác theo mặc định.

Vì thế:

$ bash -c 'x=a; echo b | read x; echo $x'
a
$ bash -c 'shopt -s lastpipe; x=a; echo b | read x; echo $x'
b

( readmapfilecó cùng một vấn đề.)

Ngoài ra (và như được đề cập bởi Attie), sử dụng thay thế quy trình , hoạt động giống như một đường ống tổng quát và được hỗ trợ trong Bash, ksh và zsh.

$ bash -c 'x=a; read x < <(echo b); echo $x'
b

POSIX không xác định được liệu các bộ phận của đường ống có chạy trong các lớp con hay không, vì vậy thực sự không thể nói rằng bất kỳ lớp vỏ nào sẽ "sai" trong điều này.


2
Nếu bạn tắt chức năng kiểm soát công việc của bash, bạn cũng có thể sử dụng Lastpipe trong vỏ tương tác:set +m; shopt -s lastpipe; x=a; echo b | read x; echo $x; set -m
Cyrus

@Cyrus, ah phải rồi, tôi đã quên chi tiết, cảm ơn
ilkkachu

9

Như Kamil đã chỉ ra, mỗi yếu tố trong đường ống là một quá trình riêng biệt.

Bạn có thể sử dụng sau quá trình thay thế để có được findđể chạy trong một tiến trình khác nhau, với mapfilesự thỉnh nguyện còn lại trong phiên dịch hiện tại của bạn, cho phép truy cập vào myarrsau đó:

myarr=()
mapfile -t myarr < <( find . -maxdepth 1  -name "mysqldump*" )
echo "${myarr[@]}"

b < <( a )sẽ hoạt động tương tự như a | bvề cách thức đường ống được nối dây - sự khác biệt là bđược thực hiện " ở đây ".

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.