2014-10-20 15 views
20

Giả sử kịch bản tôi đã sau: -Bash: Head & Tail hành vi với kịch bản bash

test.sh

#!/bin/bash 
command1 #prints 5 lines 
command2 #prints 3 lines 

tôi chạy kịch bản với test.sh|head -n5

Điều gì sẽ xảy ra trong này trường hợp? Nó sẽ chạy cả hai lệnh? hoặc nó sẽ dừng lại sau khi command1? Nếu tôi gọi nó là -n1 thì sao?

Bối cảnh: Tôi có thể hỏi một câu hỏi rất cơ bản, nhưng tôi thực sự nhận thấy điều gì đó thú vị. Kịch bản của tôi (tập lệnh khác) đã xử lý 7.000 tệp và mỗi tệp tạo ra 1 dòng đầu ra. Phải mất 7 phút để chạy kịch bản hoàn toàn nhưng làm đầu -n1 đã cho tôi nhắc nhở ngay lập tức giống như kịch bản đã chấm dứt sau khi xử lý tập tin đầu tiên chỉ

Edit: Sau đây là kịch bản của tôi

for i in $(ls filepath);do 
    echo "$i" # issue here 
    python mySript "$i" > "/home/user/output/""$i"".out" 
    fi 
done 

Xóa echo ở trên cho phép tập lệnh chạy đầy đủ 7 phút với đầu -n1, nhưng với tiếng vọng nó chỉ in dòng đầu tiên rồi thoát.

+0

Nó sẽ chạy toàn bộ lệnh và sau đó chỉ cần chuyển đầu ra tới đầu. Mọi thứ dường như hoạt động chính xác như nó cần, tôi không hiểu bạn đang hỏi gì? –

+0

điều này có vẻ là hành vi của bash vì 'echo' là lệnh nội trang. thử thay đổi 'echo' thành'/bin/echo' và xem điều gì sẽ xảy ra. – ymonad

+0

@ymonad Bạn nói đúng, bạn có thể xây dựng thêm về hành vi này không? – RedX

Trả lời

16

Đây là một vấn đề khá thú vị! Cảm ơn bạn đã đăng bài!

Tôi giả định rằng điều này xảy ra khi head thoát sau khi xử lý một vài dòng đầu tiên, do đó, SIGPIPE tín hiệu được gửi đến chạy tập lệnh khi nó cố gắng echo $x lần sau. Tôi đã sử dụng tập lệnh của RedX để chứng minh lý thuyết này:

#!/usr/bin/bash 
rm x.log 
for((x=0;x<5;++x)); do 
    echo $x 
    echo $x>>x.log 
done 

Tác phẩm này, như bạn đã mô tả! Sử dụng t.sh|head -n 2 nó chỉ ghi 2 dòng vào màn hình và x.log. Nhưng bẫy SIGPIPE hành vi này thay đổi ...

#!/usr/bin/bash 
trap "echo SIGPIPE>&2" PIPE 
rm x.log 
for((x=0;x<5;++x)); do 
    echo $x 
    echo $x>>x.log 
done 

Output:

$ ./t.sh |head -n 2 
0 
1 
./t.sh: line 5: echo: write error: Broken pipe 
SIGPIPE 
./t.sh: line 5: echo: write error: Broken pipe 
SIGPIPE 
./t.sh: line 5: echo: write error: Broken pipe 
SIGPIPE 

Lỗi ghi xảy ra như stdout đã được đóng cửa như đầu kia của ống được đóng lại. Và bất kỳ nỗ lực nào để ghi vào ống kín đều gây ra tín hiệu SIGPIPE, kết thúc chương trình theo mặc định (xem man 7 signal). Bây giờ x.log chứa 5 dòng.

Điều này cũng giải thích lý do tại sao /bin/echo giải quyết được sự cố. Xem kịch bản sau đây:

rm x.log 
for((x=0;x<5;++x)); do 
    /bin/echo $x 
    echo "Ret: $?">&2 
    echo $x>>x.log 
done 

Output:

$ ./t.sh |head -n 2 
0 
Ret: 0 
1 
Ret: 0 
Ret: 141 
Ret: 141 
Ret: 141 

Decimal 141 = hex 8D. Hex 80 nghĩa là tín hiệu được nhận, hex 0D là cho SIGPIPE. Vì vậy, khi /bin/echo cố gắng viết để stdout nó có một SIGPIPE và nó đã được chấm dứt (như hành vi mặc định) thay vì chạy kịch bản.

+0

Giải thích tuyệt vời và công việc xung quanh cũng khá gọn gàng. Tôi có thể đoán tại sao bash không xử lý tình huống như vậy. Nó có ý nghĩa để đóng đường ống và gửi SIGPIPE, nếu không các quá trình trong shell sẽ tiếp tục chạy và các lập trình viên sẽ phải xử lý các kịch bản thoát trong mọi tiến trình. Cảm ơn bạn rất nhiều! –

+0

@MangatRai: Trên thực tế [thẻ: bash] có thể xử lý tình huống bằng cách bẫy SIGPIPE. Thường đóng một đường ống trong khi quá trình khác muốn ghi vào nó là một lỗi. Vì vậy, hành vi mặc định là chấm dứt nhà văn. Nhưng nếu lập trình viên biết rằng đó không phải là lỗi thực, thì có thể thực hiện các hành động để giải quyết vấn đề. Và tình trạng này không phải là cho bash, nhưng đối với bất kỳ thực thi mà viết cho một đường ống. Nếu 'đầu' được sử dụng là" đường ống "có thể" xem xét "sau vài hàng đầu tiên thì việc chấm dứt phía nguồn của đường ống là bình thường. – TrueY

+0

Điều này đúng, y? Đã được giải thích (xin lỗi) –

1

Đây là một nhận xét sau đó là câu trả lời nhưng nó quá lớn đối với nhận xét.

tôi đã cố gắng sau kịch bản:

#!/usr/bin/env bash 

rm -f "test_head.log" 
echo "1 line" 
echo "1 line" >> "test_head.log" 
echo "2 line" 
echo "2 line" >> "test_head.log" 
echo "3 line" 
echo "3 line" >> "test_head.log" 
echo "4 line" 
echo "4 line" >> "test_head.log" 
echo "5 line" 
echo "5 line" >> "test_head.log" 
echo "6 line" 
echo "6 line" >> "test_head.log" 
echo "7 line" 
echo "7 line" >> "test_head.log" 
echo "8 line" 
echo "8 line" >> "test_head.log" 

Sau đó, tôi chạy kịch bản với:

./test_head.sh | đầu -n1

Sản lượng mèo (tôi ngạc nhiên):

1 dòng

Tôi không có ý tưởng những gì đang xảy ra.

Sau khi đọc nhận xét @ymonad tôi đã dùng thử và thay thế echo bằng /bin/echo và cách đó giải quyết được sự cố. Tôi hy vọng anh ấy có thể giải thích thêm về hành vi này.

8

Phát hiện thú vị. Theo các bài kiểm tra của tôi, nó chính xác như bạn đã nói. Ví dụ tôi có kịch bản này mà chỉ ăn cpu, để cho chúng tôi phát hiện ra nó trong top:

for i in `seq 10` 
    do echo $i 
    x=`seq 10000000` 
done 

Piping kịch bản với head -n1 chúng ta thấy lệnh trở về sau dòng đầu tiên. Đây là hành vi head: nó hoàn thành công việc của mình, vì vậy nó có thể dừng và trả lại quyền kiểm soát cho bạn.

Các kịch bản đầu vào nên tiếp tục hoạt động nhưng hãy nhìn những gì sẽ xảy ra: khi head lợi nhuận, nó pid không tồn tại nữa. Vì vậy, khi linux cố gắng để gửi đầu ra của kịch bản đến quá trình đầu, nó không tìm thấy quá trình, do đó, kịch bản bị treo và dừng lại.

Hãy thử nó với một kịch bản python:

for i in xrange(10): 
    print i 
    range(10000000) 

Khi chạy nó và đường ống để đầu bạn có điều này:

$ python -u test.py | head -n1 
0 
Traceback (most recent call last): 
    File "test.py", line 2, in <module> 
    print i 
IOError: [Errno 32] Broken pipe 

Tùy chọn -u nói python để tự động tuôn ra những stdin và stdout, như bash sẽ làm. Vì vậy, bạn thấy rằng chương trình thực sự dừng lại với một lỗi.

+1

Sử dụng tốt Python để biết thông tin chi tiết. – Davidmh

+1

Sử dụng python to root gây ra sự cố đáng yêu. –

+1

@MangatRai Thực ra tôi đã làm ngược lại, tôi nghĩ: bash? Noo, chúng ta hãy viết điều này trong python, và lỗi xuất hiện :) nhưng lần sau tôi có một vấn đề lạ với bash tôi sẽ sử dụng python một lần nữa! –

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