2012-11-13 36 views
7

Trong một kịch bản bash Tôi muốn gán đầu ra của times BUILTIN đến một biến mảng, nhưng tôi thấy không có cách nào tốt hơn so vớiBash: gán đầu ra của BUILTIN 'lần' cho một biến

tempnam=/tmp/aaa_$$_$RANDOM 
times > ${tempnam} 
mapfile -t times_a < ${tempnam} 

Tôi viết đầu ra vào một tập tin tạm thời và đọc nó trở lại trong mảng times_a bởi vì đường ống hoặc $(times) sẽ thực hiện trong một subshell và trả về các giá trị sai.

Bất kỳ giải pháp nào tốt hơn không có tệp tạm thời?

Trả lời

6

Vấn đề cơ bản mà bạn cần phải giải quyết là làm thế nào để có được cả hai việc thực hiện time và chuyển nhượng biến xảy ra trong cùng một trình bao, mà không có tệp tạm thời. Hầu như mọi phương thức Bash đều cung cấp đường ống đầu ra của một thứ đến một thứ khác, hoặc bắt đầu ra của một lệnh, có một mặt làm việc trong một vỏ con.

Dưới đây là một cách để bạn có thể làm điều đó mà không có một tập tin tạm thời, nhưng tôi sẽ cảnh báo bạn, nó không đẹp, nó không phải là cầm tay để tiện ích khác, và nó đòi hỏi ít nhất Bash 4:

coproc co { cat; }; times 1>&${co[1]}; eval "exec ${co[1]}>&-"; mapfile -tu ${co[0]} times_a 

Tôi sẽ giải thích điều này cho bạn:

coproc co { cat; } 

Điều này tạo ra một coprocess; một quy trình chạy ở chế độ nền, nhưng bạn được cung cấp đường ống để nói với đầu vào tiêu chuẩn và đầu ra tiêu chuẩn, là số FD ${co[0]} (tiêu chuẩn trong số cat) và ${co[1]} (tiêu chuẩn trong số cat). Các lệnh được thực hiện trong một subshell, vì vậy chúng ta không thể thực hiện một trong các mục tiêu của chúng ta (chạy times hoặc đọc vào một biến), nhưng chúng ta có thể sử dụng cat để truyền đầu vào thông qua đầu ra, và sau đó sử dụng đường ống đó để nói chuyện với timesmapfile trong trình bao hiện tại.

times >&${co[1]}; 

Run times, chuyển hướng tiêu chuẩn của nó ra tiêu chuẩn trong các lệnh cat.

eval "exec ${co[1]}>&-" 

Đóng đầu vào của lệnh cat. Nếu chúng tôi không làm điều này, cat sẽ tiếp tục chờ đầu vào, giữ đầu ra của nó mở và mapfile sẽ tiếp tục chờ đợi điều đó, làm cho hệ vỏ của bạn bị treo. exec, khi không có lệnh nào, chỉ cần áp dụng các chuyển hướng của nó đến trình bao hiện tại; chuyển hướng đến - đóng một FD. Chúng ta cần sử dụng eval vì Bash có vẻ gặp rắc rối với exec ${co[1]}>&-, giải thích FD làm lệnh thay vì một phần của chuyển hướng; sử dụng eval cho phép biến đó được thay thế đầu tiên, và sau đó được thực thi.

mapfile -tu ${co[0]} times_a 

Cuối cùng, chúng tôi thực sự đọc dữ liệu từ tiêu chuẩn ra khỏi coprocess. Chúng tôi đã quản lý cả hai lệnh times và lệnh mapfile trong trình bao này và không sử dụng tệp tạm thời, mặc dù chúng tôi đã sử dụng quy trình tạm thời làm đường dẫn giữa hai lệnh.

Lưu ý rằng điều này có một cuộc đua tinh tế. Nếu bạn thực hiện các lệnh này một, thay vì tất cả là một lệnh, lệnh cuối cùng không thành công; bởi vì khi bạn đóng tiêu chuẩn cat s, nó sẽ thoát ra, khiến cho coprocess thoát ra và FD bị đóng. Dường như khi được thực hiện trên cùng một dòng, mapfile được thực hiện đủ nhanh để coprocess vẫn mở khi nó chạy, và do đó nó có thể đọc từ đường ống; nhưng tôi có thể may mắn. Tôi đã không tìm ra một cách tốt đẹp xung quanh này.

Tất cả đã nói, nó đơn giản hơn nhiều chỉ để viết ra tệp tạm thời. Tôi sẽ sử dụng mktemp để tạo ra một tên tập tin, và nếu bạn đang ở trong một kịch bản, thêm một cái bẫy để đảm bảo rằng bạn dọn dẹp tempfile của bạn trước khi thoát:

tempnam=$(mktemp) 
trap "rm '$tempnam'" EXIT 
times > ${tempnam} 
mapfile -t times_a < ${tempnam} 
+1

Tôi ước gì có thể upvote này thậm chí nhiều hơn, tôi đã học được rất nhiều đọc này, câu trả lời thực sự tốt đẹp! – higuaro

0

Một khả năng để làm điều gì đó tương tự sẽ

times > >(other_command) 

Đây là một chuyển hướng đầu ra combided với một quá trình thay thế. Bằng cách này, times được thực hiện trong trình bao hiện tại và đầu ra được chuyển hướng đến một tiến trình con mới. Vì vậy, tuy nhiên làm điều này với mapfile không có ý nghĩa nhiều, vì lệnh đó sẽ không được thực hiện trong cùng một trình bao, và đó có lẽ không phải là những gì bạn sẽ muốn. Tình huống này hơi phức tạp một chút, vì bạn không thể gọi tới các nội trang hệ vỏ được thực thi trong một vỏ bọc con.

1

Câu hỏi hay, hay.

Cải tiến sẽ là sử dụng mktemp, do đó bạn không dựa vào tính ngẫu nhiên để giữ cho tệp của bạn độc đáo.

TMPFILE=$(mktemp aaa_XXXXXXXXXX) 
times > "$TMPFILE" 
mapfile -t times_a < ${tempnam} 
rm "$TMPFILE" 

Ngoài ra, tôi sử dụng thay vì mapfile (vì tôi không có mapfile).

a=0; for var in $(cat "$TMPFILE"); do ((a++)); TIMES_A[$a]=$var; done 

Nhưng, vâng, tôi không thấy cách bạn có thể làm điều đó mà không có tệp hoặc ống được đặt tên.

Các vấn đề liên quan