Hoàn toàn đầu ra lệnh đệm trước khi chuyển sang lệnh khác?


10

Có cách nào để chỉ thực hiện một lệnh sau khi một lệnh khác được thực hiện mà không có tệp tạm thời không? Tôi có một lệnh chạy dài hơn và một lệnh khác định dạng đầu ra và gửi nó đến máy chủ HTTP bằng cách sử dụng curl. Nếu tôi chỉ thực hiện commandA | commandB, commandBsẽ bắt đầu curl, kết nối với máy chủ và bắt đầu gửi dữ liệu. Vì commandAmất quá nhiều thời gian, máy chủ HTTP sẽ hết thời gian. Tôi có thể làm những gì tôi muốn vớicommandA > /tmp/file && commandB </tmp/file && rm -f /tmp/file

Vì tò mò tôi muốn biết liệu có cách nào để làm điều đó mà không cần tệp tạm thời. Tôi đã thử mbuffer -m 20M -q -P 100nhưng quá trình uốn tóc vẫn được bắt đầu ngay từ đầu. Mbuffer đợi cho đến khi commandAhoàn thành việc gửi dữ liệu. (Bản thân dữ liệu chỉ tối đa vài trăm kb)


Thế còn commandA && commandB?
Eyoung100

1
mà không truyền đầu ra của commandAđể commandB, đúng không?
Josef nói phục hồi Monica

Nó khởi động Lệnh B nếu Lệnh A hoàn thành thành công, điều đó có nghĩa là curl không bắt đầu sớm.
Eyoung100

2
@ eyoung100 nhưng nó không chuyển stdout từ lệnhA sang stdin cho lệnhB, đó là những gì Josef cần!
roaima

Nếu anh ta muốn đầu ra được thông qua, anh ta phải sử dụng một tập tin. Xem câu trả lời của cuonglm.
Eyoung100

Câu trả lời:


14

Điều này tương tự như một vài câu trả lời khác. Nếu bạn có gói của More moreilsils, bạn nên có spongelệnh. Thử

commandA | sponge | { IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; }

Các spongelệnh cơ bản là một pass-through lọc (như cat) ngoại trừ việc nó không bắt đầu viết đầu ra cho đến khi nó có đọc toàn bộ đầu vào. Tức là, nó hấp thụ dữ liệu, và sau đó giải phóng nó khi bạn bóp nó (giống như một miếng bọt biển). Vì vậy, ở một mức độ nhất định, đây là trò lừa đảo trực tuyến - nếu có một lượng dữ liệu không hề nhỏ, spongegần như chắc chắn sử dụng một tệp tạm thời. Nhưng nó vô hình với bạn; bạn không phải lo lắng về những việc dọn phòng như chọn một tên tệp duy nhất và dọn dẹp sau đó.

Việc { IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; } đọc dòng đầu tiên từ sponge. Hãy nhớ rằng, điều này không xuất hiện cho đến khi commandAkết thúc.  Sau đó, nó kích hoạt commandB, ghi dòng đầu tiên vào đường ống và gọi catđể đọc phần còn lại của đầu ra và ghi nó vào đường ống.


cảm ơn! Vì vậy, spongelàm thế điều tôi đã sử dụng mbufferkiếm, nhưng dường như phù hợp hơn ở đây. sử dụng đọc là thông minh ở đây. Chắc chắn sẽ nhớ điều này cho tương lai.
Josef nói Phục hồi Monica

@Josef: Tôi chưa bao giờ nghe nói mbuffertrước đây; nó thực sự có thể cũng chẳng thua kém sponge. Tôi đồng ý rằng sử dụng readlà một mẹo thông minh. Tôi không thể lấy tín dụng đầy đủ cho nó; đôi khi nó xuất hiện trong các câu trả lời trong suốt Stack Exchange (U & L, Super User, Ask Ubuntu, v.v.). Trên thực tế, câu trả lời của roaima cho câu hỏi này rất giống với câu hỏi của tôi ngoại trừ nó không sử dụng sponge(hoặc một cái gì đó tương đương), vì vậy, như tôi đã đề cập trong một bình luận, nó không trì hoãn bắt đầu commandBnhiều như bạn cần (theo cách hiểu của tôi về vấn đề của bạn).
G-Man nói 'Phục hồi Monica'

github.com/ildar-shaimordanov/perl-utils#sponge có phiên bản tập lệnh của "miếng bọt biển" được bọc trong hàm bash.
marinara

5

Các lệnh trong đường ống được bắt đầu đồng thời, bạn cần lưu trữ commandAđầu ra ở đâu đó để sử dụng sau. Bạn có thể tránh tệp tạm thời bằng cách sử dụng biến:

output=$(command A; echo A)
printf '%s' "${output%%A}" | commandB

Mục đích của sự echo Athay thế trong quá trình là gì?
Chấn thương kỹ thuật số

3
@DigitalTrauma: Nó ngăn thay thế lệnh từ bỏ các dòng mới.
cuonglm 04/03/2015

À, tôi hiểu rồi, đúng vậy - nắm bắt tốt một trường hợp góc có thể xảy ra
Chấn thương kỹ thuật số

Tôi nhận ra câu trả lời của tôi là không chính xác, vì vậy tôi đã xóa nó. Tôi nghĩ rằng điều này có vẻ đúng thay thế. +1
Chấn thương kỹ thuật số

Cảm ơn rất nhiều! Đó là điều tuyệt vời và đặc biệt là tiếng vang A / %% A để giữ dòng mới (ngay cả khi tôi không yêu cầu điều đó)
Josef nói Phục hồi Monica

1

Tôi không biết bất kỳ tiện ích UNIX tiêu chuẩn nào có thể giải quyết vấn đề này. Một tùy chọn sẽ được sử dụng awkđể tích lũy commandAđầu ra và tuôn ra nó commandBtrong một lần chụp, như vậy

commandA  | awk '{x = x ORS $0}; END{printf "%s", x | "commandB"}'

Coi chừng việc này có thể tốn nhiều bộ nhớ vì awkđang xây dựng một chuỗi từ đầu vào của nó.


1
Điều này thoát khỏi dòng mới nhất. Giả sử rằng ký tự cuối cùng một dòng mới, bạn cần một \nhoặc một cái khác ORSở cuối.
G-Man nói 'Tái lập lại' '

0

Bạn có thể giải quyết yêu cầu với một kịch bản nhỏ. Biến thể đặc biệt này tránh được tệp tạm thời và bộ nhớ tiềm năng với chi phí của các quy trình bổ sung.

#!/bin/bash
#
IFS= read LINE

if test -n "$LINE"
then
    test -t 2 && echo "Starting $*" >&2
    (
        echo "$LINE"
        cat

    ) | "$@"
else
    exit 0
fi

Nếu bạn gọi kịch bản waituntil(và làm cho nó có thể thực thi được, hãy đặt nó vào PATH, v.v.), bạn sẽ sử dụng nó như thế này

commandA... | waituntil commandB...

Thí dụ

( sleep 3 ; date ; id ) | waituntil nl

1
Tất cả điều này là sự chậm trễ bắt đầu commandBcho đến khi commandAđã viết dòng đầu ra đầu tiên của nó . Điều đó có lẽ không đủ tốt.
G-Man nói 'Tái lập Monica'

@ G-Man nhưng chúng tôi không biết. Tôi thích của bạn sponge. Tắt để đọc lên cái đó ngay bây giờ.
roaima
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.