Lặp lại các dòng của hai tệp song song [đóng]


18

Đối tượng của kịch bản tôi đang thực hiện là so sánh hai loạt tệp. Tên tệp được lưu trữ thành hai tệp riêng biệt, một đường dẫn trên mỗi dòng. Ý tưởng của tôi là có hai while readvòng lặp, một vòng cho mỗi danh sách tên tệp, nhưng làm thế nào tôi có thể trộn hai vòng với nhau?

while read compareFile <&3; do     
 if [[ ! $server =~ [^[:space:]] ]] ; then  #empty line exception
    continue
 fi   
    echo "Comparing file - $compareFile"
 if diff "$compareFile" _(other file from loop?_) >/dev/null ; then
     echo Same
 else
      echo Different
 fi 
done 3</infanass/dev/admin/filestoCompare.txt

Tôi cần có thể so sánh các tệp từ hai danh sách khác nhau cùng một lúc qua hai vòng trong khi đọc các vòng lặp ... Điều này thậm chí có thể không?


Bạn có ý định thực hiện kịch bản này như một vị vua thách thức? Nếu không phải như vậy, các công cụ mạnh mẽ đã tồn tại để so sánh các tệp, như diff.
lgeorget

" loại thử thách", xin lỗi
lgeorget

@lgeorget OP đang sử dụng diff.
terdon

ah, tập tin từ hai danh sách. Xin lỗi vì những bình luận vô ích ...
lgeorget

Vui lòng tránh đăng bài chéo
iruvar

Câu trả lời:


20

Bạn không cần hai vòng lặp; bạn chỉ cần đọc từ hai tệp trong một vòng lặp.

while read compareFile1 <&3 && read compareFile2 <&4; do     
 if [[ ! $server =~ [^[:space:]] ]] ; then  #empty line exception
    continue
 fi   
    echo "Comparing file - $compareFile"
 if diff "$compareFile1" "$compareFile2" >/dev/null ; then
     echo Same
 else
      echo Different
 fi 
done 3</infanass/dev/admin/filestoCompare.txt 4<other_file

Đó là rất nhiều mã ít hơn nhờ! Làm thế nào để tôi xử lý một ngoại lệ dòng trống cùng một lúc sau đó cho hai vòng lặp?
mkrouse

@mkrouse, tôi không biết bạn đã làm gì với biến máy chủ $ đó trước đó, tuy nhiên bạn kiểm tra một dòng trống trên một biến, bạn chỉ làm tương tự với ...
psusi

7

Phương pháp 1: sử dụng những gì bạn biết

Vì bạn đã biết cách lặp qua một tệp, bạn có thể kết hợp các tệp và sau đó xử lý các tệp kết hợp. Lệnh pastenối hai tệp theo từng dòng. Nó đặt một tab giữa các dòng đến từ hai tệp, vì vậy giải pháp này giả định rằng không có tab nào trong tên tệp của bạn. (Bạn có thể thay đổi dấu phân cách nhưng bạn phải tìm một ký tự không có trong tên tệp.)

paste -- "$list1.txt" "list2.txt" |
while IFS=$'\t' read -r file1 file2 rest; do
  diff -q -- "$file1" "$file2"
  case $? in
    0) status='same';;
    1) status='different';;
    *) status='ERROR';;
  esac
  echo "$status $file1 $file2"
done

Nếu bạn muốn bỏ qua các dòng trống, bạn cần phải làm riêng từng tệp, vì pastecó thể khớp một dòng trống từ một tệp với một dòng không trống từ một tệp khác. Bạn có thể sử dụng grepđể lọc các dòng không trống.

paste -- <(grep '[^[:space:]]' "$list1.txt") <(grep '[^[:space:]]' "list2.txt") |
while IFS=$'\t' read -r file1 file2 rest; do
  

Lưu ý rằng nếu hai tệp có độ dài khác nhau, bạn sẽ nhận được một sản phẩm nào $file2(bất kể danh sách nào kết thúc trước).

Phương pháp 2: lặp qua hai tệp

Bạn có thể đặt một lệnh phức tạp như bạn muốn trong điều kiện của vòng lặp while. Nếu bạn đặt read file1 <&3 && read file2 <&4thì vòng lặp sẽ chạy miễn là cả hai tệp có một dòng để đọc, tức là cho đến khi một tệp hết.

while read -u 3 -r file1 && read -u 4 -r file2; do
  
done 3<list1..txt 4<list2.txt

Nếu bạn muốn bỏ qua các dòng trống, nó phức tạp hơn một chút, vì bạn phải bỏ qua hai tệp một cách độc lập. Cách dễ dàng là chia vấn đề thành hai phần: bỏ qua các dòng trống từ một tệp và xử lý các dòng không trống. Một phương pháp để bỏ qua các dòng trống là xử lý thông qua grepnhư trên. Coi chừng không gian cần thiết giữa <toán tử chuyển hướng và <(lệnh bắt đầu một lệnh điều chỉnh.

while read -u 3 -r file1 && read -u 4 -r file2; do
  
done 3< <(grep '[^[:space:]]' "$list1.txt") 4< <(grep '[^[:space:]]' "list2.txt")

Một phương pháp khác là viết một hàm hoạt động như thế readnhưng bỏ qua các dòng trống. Chức năng này có thể hoạt động bằng cách gọi readtrong một vòng lặp. Nó không phải là một hàm, nhưng một hàm là cách tiếp cận tốt nhất, cả để tổ chức mã của bạn và bởi vì đoạn mã đó cần phải được gọi hai lần. Trong hàm, ${!#}là một thể hiện của cấu trúc bash ${!VARIABLE}ước tính giá trị của biến có tên là giá trị của VARIABLE; ở đây biến là biến đặc biệt #chứa số lượng tham số vị trí, vì vậy ${!#}là tham số vị trí cuối cùng.

function read_nonblank {
  while read "$@" &&
        [[ ${!#} !~ [^[:space:]] ]]
  do :; done
}
while read_nonblank -u 3 -r file1 && read_nonblank -u 4 -r file2; do
  
done 3<list1..txt 4<list2.txt

Tôi thích việc sử dụng -utùy chọn đọc
Felipe Alvarez

1

Một cách tiếp cận sẽ là sử dụng read -rathay vì chỉ read. Giả sử filestoCompare.txt2 cột chứa với các tên tệp trong mỗi read -racột , chúng sẽ đọc cả hai cột cùng một lúc và gán chúng vào một mảng , compareFile. Mảng này sau đó có thể được truy cập để chỉ mục 0 sẽ là tệp đầu tiên và chỉ mục 1 sẽ là tệp thứ 2 mỗi lần qua whilevòng lặp.

Thí dụ

Nói rằng tôi có tệp này : filestoCompare.txt, và nó chứa các mục sau:

file1 file2
file3 file4
file5 file6

Lệnh đi qua tệp này sẽ như sau:

$ while read -ra a ; do printf "%s\t%s\n" ${a[0]} ${a[1]}; done < filestoCompare.txt
file1   file2
file3   file4
file5   file6

Nếu 2 tệp thực sự là các tệp riêng biệt, chẳng hạn như:

#list1
file1
file2
file3

#list2
file4
file5
file6

Chúng có thể được nối với nhau bằng pastelệnh như vậy:

$ paste list1 list2 > list1and2

Đây là nội dung của list1and2:

$ cat list1and2
file1   file4
file2   file5
file3   file6

Nhưng đó không phải là định dạng đầu vào: danh sách nằm trong hai tệp khác nhau. Bạn có thể joinhọ đầu tiên.
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles - Tôi biết đó không phải là định dạng đầu vào, tôi tin rằng tôi thậm chí đã nói rằng "... Giả sử filestoCompare.txt chứa 2 cột với tên tệp trong mỗi ...". Tôi hiểu sự tranh chấp của bạn và không đồng ý. OP đã không cung cấp bất kỳ hướng dẫn nào thêm về câu hỏi này kể từ khi đăng nó.
slm

@Gilles - nếu tôi thêm một ví dụ cho thấy cách sử dụng lệnh pasteđể tham gia 2 tệp thì sao? Điều đó sẽ đưa bạn đến undownvote?
slm
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.