2015-06-01 18 views
6

Giả sử, tôi có hai tệp.Shell Script - đọc hai dòng tệp và đọc một dòng của một tệp khác

File1 chứa:

line1 
line2 
line3 
line4 
line5 
line6 
line7 
line8 
line9 
line10 

và File2 chứa:

anotherline1 
anotherline2 
anotherline3 

tôi muốn làm một vòng lặp while để nó kết quả đầu ra này thành một file3:

line1 
line2 
anotherline1 
line3 
line4 
anotherline2 
line5 
line6 
anotherline3 
line7 
line8 
anotherline1 
line9 
line10 
anotherline2 

tôi đã tìm kiếm ở đây cho một câu trả lời nhưng tôi không thể tìm ra cách để làm điều đó. Tôi phải nói rằng tôi khá noob ở kịch bản lệnh vỏ ...

Ngoài ra: "Vòng lặp while" nên đến khi kết thúc tập tin1. Không có vấn đề bao lâu là File2, trên thực tế, trong kịch bản cá nhân của tôi, File2 sẽ luôn luôn ngắn hơn File1. Đầu ra phải là: File1Line1, File1Line2, File2Line1, File1Line3, File1Line4, File2Line2 .... vv

Bất kỳ trợ giúp nào?

lưu ý: nếu điều này đã được trả lời trước đó, thì các bản mod vui lòng đóng câu hỏi này. Tôi không thể tìm thấy bất kỳ câu trả lời nào sau khi googling trong vài giờ.

EDIT: Đã thêm làm rõ thời điểm kết thúc vòng lặp.

+0

BTW - làm cách nào bạn muốn điều này hoạt động nếu số dòng trong hai tệp đầu vào không khớp (một dòng trong tệp2 cho mỗi hai dòng trong tệp1)? –

+0

"Vòng lặp while" nên cho đến khi kết thúc tập tin1. Không có vấn đề bao lâu là File2, trên thực tế, trong kịch bản cá nhân của tôi, File2 sẽ luôn luôn ngắn hơn File1. Đầu ra phải là: File1Line1, File1Line2, File2Line1, File1Line3, File1Line4, File2Line2 .... vv – user3246010

+0

Bạn có thể xem xét chỉnh sửa điều đó vào câu hỏi của mình; nó thay đổi đáng kể những gì tạo thành một câu trả lời đúng. –

Trả lời

3

Nếu tôi giải thích câu hỏi của bạn một cách chính xác, bạn muốn

  1. bước qua file1 bắt đầu kết thúc và
  2. chèn dòng "tiếp theo" từ file2 sau mỗi hai dòng, quay lại đầu trang file2 bất cứ khi nào bạn hết nội dung.

Nếu đó là mục tiêu, bạn có thể làm điều đó với awk theo một số cách, nhưng đoạn mã sau đây có thể sẽ làm.

#!/usr/bin/env bash 

mapfile -t file2 < file2 

max=${#file2[@]} 

while read line; do 
    ((count++)) 
    echo "$line" 
    if [[ $((count%2)) == 0 ]]; then 
    if [[ next -ge max ]]; then 
     next=0 
    fi 
    echo "${file2[next++]}" 
    fi 
done < file1 

Điều này có phần tải xuống tất cả file2 vào bộ nhớ dưới dạng mảng. Nếu đó là một vấn đề bởi vì các tập tin là quá lớn, bạn có lẽ có thể thêm một số glop vỏ để đi tìm dòng cụ thể tại mỗi invocation của vòng lặp, mặc dù đó sẽ rất chậm. Tốt hơn, hãy xác định vấn đề của bạn cụ thể hơn hoặc xem xét các công cụ thay thế! :-)

Lưu ý rằng lệnh mapfile là bash-4-ism, vì vậy nếu bạn đang sử dụng Linux cũ hoặc OSX (vẫn còn đi kèm với bash 3), bạn sẽ cần một giải pháp khác. .. hoặc nâng cấp để bash.

+0

Bạn nhận được hành vi "quay lại" ở đâu? Tôi không thấy bất kỳ đặc điểm kỹ thuật nào cả (ngoài chương trình đó, không được thoát ra tại thời điểm đó). –

+0

Có thể xem xét xác định rõ ràng nhu cầu bash 4.x ở đây, được sử dụng 'mapfile'. –

+0

(BTW, có _are_ cách dễ dàng để mở lại tập tin2 khi kết thúc nó; một ví dụ có thể đọc «& 3 || {exec 3

2

Tôi không biết cách nào thuận tiện để thực hiện điều gì đó cụ thể trong tập lệnh shell thuần túy. Bạn có lẽ tốt hơn hết chỉ cần viết một python/perl/etc. kịch bản. Một cái gì đó giống như kịch bản awk này sẽ làm việc, nhưng nó hơi lộn xộn:

awk '{ 
    print; getline; print; 
    if (!getline < "file2") { 
    close("file2"); 
    getline < "file2" 
    }; 
    print 
}' file1 

Các print; getline; print sẽ in dòng tiếp theo từ file1, có được những dòng sau đó, và sau đó in nó. if (!getline < "file2") cố gắng đọc dòng tiếp theo từ tệp2 và nếu nó không thành công, đóng nó, ngầm mở lại nó và đọc dòng đầu tiên từ tệp2. Cuối cùng print sẽ in dòng đó từ tệp 2 (tiếp theo hoặc đầu tiên).

+0

Giải thích về cách hoạt động của một lớp lót sẽ mang tính giáo dục cao! :-) – ghoti

+0

Eh? Không có lý do này không thể được thực hiện trong vỏ. –

+0

Vì vậy, nếu tôi thêm > file3 vào cuối, đầu ra sẽ là những gì tôi muốn? Tôi sẽ thử điều đó ngay bây giờ ... – user3246010

1

Dễ dàng thực hiện trong trình bao gốc - không cần các công cụ bên ngoài như awk. Mở hai bộ mô tả tệp, gắn với các tệp khác nhau và lặp lại cả hai.

while :; do 
    read -r line1 <&3 && printf '%s\n' "$line1" || break 
    read -r line2 <&3 && printf '%s\n' "$line2" || break 
    read -r line3 <&4 || { exec 4<file2 && read -r line3 <&4; } 
    printf '%s\n' "$line3" 
done 3<file1 4<file2 1>file3 

Trong trường hợp này, 3 là số mô tả tập tin mà chúng tôi đã mở file14 là số mô tả tập tin mà chúng tôi đã mở file2. 1>file3 hoặc đơn giản là >file3, định tuyến lại đầu ra trên bộ mô tả tệp 1 (còn gọi là stdout) cho tệp đầu ra.

Bạn có thể chọn các số khác nếu bạn thích, mặc dù chỉ phạm vi từ 3-9 được đảm bảo là miễn phí để sử dụng trên tất cả các vỏ POSIX. (0-2 là stdin, stdout và stderr; trên 10 có thể được sử dụng bởi shell để sử dụng riêng của nó, mặc dù điều này hiếm khi xảy ra trong vỏ mới hơn và hiện đại hơn).

+0

Điều này (ví dụ đầu tiên) là khá nhiều những gì tôi muốn. Mặc dù thời gian kết thúc cũng nếu đạt đến kết thúc của File2, và tôi muốn nó giữ cho nó đi cho đến khi tất cả các dòng của File1 được đọc. – user3246010

+0

Tiếp tục và làm gì? Thêm dòng trống trong đó nội dung từ tệp 2 nếu không sẽ là? Chỉ cần thêm nhiều dòng từ file1? –

+0

Ví dụ đầu tiên được sửa đổi để bỏ qua EOF trên tệp2. –

2

awk này nên làm việc:

awk 'FNR==NR{a[++i]=$0; next} FNR%2{p=$0; curr=(FNR==1 || curr==i) ? 1 : curr+1; next} 
    {print p ORS $0 ORS a[curr]}' file2 file1 
line1 
line2 
anotherline1 
line3 
line4 
anotherline2 
line5 
line6 
anotherline3 
line7 
line8 
anotherline1 
line9 
line10 
anotherline2 

EDIT: Thậm chí ngắn hơn awk (nhờ @geirha):

awk 'FNR==NR{a[n++]=$0;next}1;!(FNR%2){print a[i++%n]}' file2 file1 
+2

Đây là phiên bản được chơi gôn, để giải trí 'awk' FNR == NR {a [n ++] = $ 0; tiếp theo} 1;! (FNR% 2) {in [i ++% n]} 'file2 file1' – geirha

+0

Chà là thậm chí còn tốt hơn (được thêm vào câu trả lời). – anubhava

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