Bash - ghép từng dòng tệp


10

Câu hỏi này liên quan chặt chẽ đến điều này và câu hỏi này . Tôi có một tệp chứa một vài dòng trong đó mỗi dòng là một đường dẫn đến một tệp. Bây giờ tôi muốn ghép từng dòng với từng dòng khác nhau (không phải chính nó). Ngoài ra một cặp A Bbằng với một B Acặp cho mục đích của tôi, vì vậy chỉ nên tạo một trong những kết hợp này.

Thí dụ

files.dat đọc như thế này trong một ký hiệu viết tắt, mỗi chữ cái là một đường dẫn tệp (tuyệt đối hoặc tương đối)

a
b
c
d
e

Sau đó, kết quả của tôi sẽ trông giống như thế này:

a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

Tốt nhất là tôi muốn giải quyết điều này trong bash. Không giống như các câu hỏi khác, danh sách tệp của tôi khá nhỏ (khoảng 200 dòng), vì vậy sử dụng các vòng lặp và dung lượng RAM không gây ra vấn đề gì.


Liệu nó có phải trong bash thích hợp, hoặc chỉ một cái gì đó có sẵn thông qua dòng lệnh bash? Các tiện ích khác được định vị tốt hơn để xử lý văn bản.
Jeff Schaller

@JeffSchaller Một cái gì đó có thể truy cập thông qua dòng lệnh bash. Tôi đã có một chút không rõ ràng, xin lỗi
Enno

Điều này gần như trở thành một Code Golf : P
Richard de Wit

3
Theo nguyên tắc chung, miễn là bạn cần làm một việc gì đó không tầm thường, hãy sử dụng ngôn ngữ kịch bản yêu thích của bạn trên BASH. Nó sẽ ít dễ vỡ hơn (ví dụ, đối với các ký tự hoặc khoảng trắng đặc biệt) và dễ dàng mở rộng hơn bất cứ khi nào bạn cần (nếu bạn cần ba hoặc lọc một số trong số chúng đi). Python hoặc Perl nên được cài đặt trong hầu hết mọi hộp Linux, vì vậy chúng là những lựa chọn tốt (trừ khi bạn đang làm việc trên các hệ thống nhúng, như Busybox).
Davidmh

Câu trả lời:


7

Sử dụng lệnh này:

awk '{ name[$1]++ }
    END { PROCINFO["sorted_in"] = "@ind_str_asc"
        for (v1 in name) for (v2 in name) if (v1 < v2) print v1, v2 }
        ' files.dat

PROCINFOcó thể là một gawkphần mở rộng. Nếu bạn awkkhông hỗ trợ nó, chỉ cần bỏ PROCINFO["sorted_in"] = "@ind_str_asc"dòng và đưa đầu ra vào sort(nếu bạn muốn đầu ra được sắp xếp).

(Điều này không yêu cầu đầu vào được sắp xếp.)


8
$ join -j 2 -o 1.1,2.1 file file | awk '!seen[$1,$2]++ && !seen[$2,$1]++'
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

Điều này giả định rằng không có dòng nào trong tệp đầu vào chứa bất kỳ khoảng trắng nào. Nó cũng giả định rằng các tập tin được sắp xếp .

Các joinlệnh tạo ra các sản phẩm chéo đầy đủ các dòng trong tập tin. Nó thực hiện điều này bằng cách nối tệp với chính nó trên một trường không tồn tại. Không chuẩn -j 2có thể được thay thế bằng -1 2 -2 2(nhưng không phải -j2trừ khi bạn sử dụng GNU join).

Các awklệnh đọc kết quả của việc này và chỉ kết quả đầu ra kết quả mà là cặp mà vẫn chưa được nhìn thấy.


Bạn có ý nghĩa gì bởi "tập tin được sắp xếp"? Sắp xếp theo tiêu chí nào?
Enno

@Enno Sắp xếp theo cách sort -bsẽ sắp xếp nó. joinyêu cầu sắp xếp các tập tin đầu vào.
Kusalananda

8

Một pythongiải pháp. Tệp đầu vào được đưa đến itertools.combinationstừ thư viện chuẩn, tạo ra các bộ dữ liệu 2 chiều dài được định dạng và in thành đầu ra tiêu chuẩn.

python3 -c 'from itertools import combinations
with open("file") as f:
    lines = (line.rstrip() for line in f)
    lines = ("{} {}".format(x, y) for x, y in combinations(lines, 2))
    print(*lines, sep="\n")
'

6

Nếu bạn đã rubycài đặt:

$ ruby -0777 -F'\n' -lane '$F.combination(2) { |c| puts c.join(" ")}' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
  • -0777 làm mờ toàn bộ tệp (nên ổn vì được đề cập trong OP rằng kích thước tệp nhỏ)
  • -F'\n'phân chia dựa trên dòng mới, vì vậy mỗi dòng sẽ là một phần tử trong $Fmảng
  • $F.combination(2)tạo 2các yếu tố kết hợp tại một thời điểm
  • { |c| puts c.join(" ")} in theo yêu cầu
  • nếu tập tin đầu vào có thể chứa các bản sao, hãy sử dụng $F.uniq.combination(2)


cho 3 yếu tố cùng một lúc:

$ ruby -0777 -F'\n' -lane '$F.combination(3) { |c| puts c.join(" ")}' ip.txt
a b c
a b d
a b e
a c d
a c e
a d e
b c d
b c e
b d e
c d e


Với perl(không chung chung)

$ perl -0777 -F'\n' -lane 'for $i (0..$#F) {
                             for $j ($i+1..$#F) { 
                               print "$F[$i] $F[$j]\n" } }' ip.txt
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e


Với awk

$ awk '{ a[NR]=$0 }
       END{ for(i=1;i<=NR;i++)
              for(j=i+1;j<=NR;j++)
                print a[i], a[j] }' ip.txt 
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e

5

Đây là một trong vỏ nguyên chất.

test $# -gt 1 || exit
a=$1
shift
for f in "$@"
do
  echo $a $f
done
exec /bin/sh $0 "$@"

Thí dụ:

~ (137) $ sh test.sh $(cat file.dat)
a b
a c
a d
a e
b c
b d
b e
c d
c e
d e
~ (138) $ 

1
Lệnh dải thay trailing dòng mới, vì vậy bạn nên có một cái gì đó giống như <file.dat xargs test.shhơntest.sh $(cat file.dat)
iruvar

1

Sử dụng Perlchúng ta có thể làm điều đó như được hiển thị:

$ perl -lne '
     push @A, $_}{
     while ( @A ) {
        my $e = shift @A;
        print "$e $_" for @A;
     }
' input.txt
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.