Shell scripting, lặp qua các thư mục


1

Tôi có một loạt lớn các thư mục con trên máy Mac của mình với số lượng tệp csv ngẫu nhiên trong mỗi tệp. Những gì tôi muốn làm là hợp nhất các tệp này thành một tệp cho mỗi thư mục.

Cho đến nay tôi biết tôi có thể hợp nhất các tệp này với cat * > mergedfile.csv, nhưng tôi gặp vấn đề lặp qua tất cả các thư mục. Tôi bằng cách nào đó đã xoay sở để hợp nhất tất cả mọi thứ cho đến nay, nhưng dường như tôi không thể làm điều này làm những gì tôi muốn chính xác.

Bất kỳ ý tưởng về cách tốt nhất để làm điều này?

for DIR in ./subfolder/*
do
    cat $dir/* > merged.csv 
done

1
$DIR$dirkhông giống nhau
choroba

Câu trả lời:


4

Với find, bạn có thể liệt kê đệ quy tất cả các tệp phù hợp với một tiêu chí nhất định, ví dụ như tên tệp.

for file in $(find . -type f -name "*.csv"); do cat "$file" >> /path/to/output.csv; done

Phá vỡ nó, find . -name "*.csv"sẽ tìm thấy tất cả các tệp CSV từ thư mục hiện tại bạn đang ở ( .) và vòng lặp sẽ chỉ lặp lại trên danh sách đó, nối thêm mọi thứ vào output.csvtệp.

Nhưng: Tên tệp có dấu cách, ký tự toàn cầu và dòng mới có thể khó ở đây. Một giải pháp an toàn hơn là chỉ sử dụng execcho lệnh find.

find . -name "*.txt" -exec cat '{}' >> /path/to/output.csv ';'

Ở đây, '{}'sẽ được thay thế bằng find với tên tệp. Đối với một câu hỏi dài về lý do tại sao điều này là và làm thế nào để phá vỡ vấn đề có thể được tìm thấy ở đây .

Bây giờ, nếu bạn muốn tạo một tệp CSV cho mỗi thư mục - xin lỗi, trước đây bạn không thấy điều đó - có lẽ tôi đã làm một cái gì đó như thế này:

for dir in $(find . -type d); do find $dir -maxdepth 1 -name "*.csv" -exec cat {} >> "$dir/out" ';'; mv "$dir/out" "$dir/merged.csv"; done

Mặc dù giải pháp của Franck dưới đây có lẽ hiệu quả hơn.


Tất nhiên, chú ý đến sự khác biệt giữa >>>. Cái trước sẽ luôn cắt ngắn tập tin về độ dài bằng không trước khi ghi vào nó, trong khi cái sau sẽ chỉ nối vào tập tin.

Lý do tại sao cat *.csv > merged.csvhoạt động của Haiti và tại sao trong vòng lặp của bạn, nó sẽ không hoạt động bởi vì shell sẽ mở rộng ký tự đại diện trước đó, vì vậy về cơ bản nó thấy:

cat file1.csv file2.csv file3.csv > merged.csv

Tất nhiên sẽ không ghi đè lên bất cứ điều gì.


1

Vào thư mục mẹ:

for dir in $(find . -type d); do
  cd $dir
  [[ $(ls *.csv|wc -l) -eq 0 ]] 2> /dev/null || { print "$dir.csv created";
                                                  cat *.csv > $dir.csv; }
  cd - > /dev/null
done

1

Giả sử bash 4+ (kiểm tra với bash --version), bạn có thể kích hoạt globalstar với shopt -s globstarvà lặp qua tất cả các thư mục (và chỉ các thư mục - các /quy tắc thoát ra khỏi các tệp) theo cách đệ quy với**/

for f in **/; do cat "$f"/*.csv > "$f"/merged.csv; done

Nếu bạn thực sự muốn sử dụng tất cả các tệp trong một thư mục, thay vì chỉ những tệp kết thúc bằng .csv, thì

for f in **/; do cat "$f"/* > "$f"/merged.csv; done

Nếu bạn chỉ muốn đi xuống một cấp độ, thay vì đệ quy đầy đủ, thì hãy sử dụng */chứ không phải **/.

Lỗi chính trong tập lệnh OP (ngoài việc quên bash là phân biệt chữ hoa chữ thường) là nó cố ghi nội dung của tất cả các tệp vào một .csvtệp và thực hiện theo cách mà mỗi lần lặp của vòng lặp sẽ kết thúc -viết cuối cùng.

Nếu bạn muốn ghép tất cả các .csvtệp đệ quy thành một tệp duy nhất, bạn có thể sử dụng lại sao

for f in **/*.csv; do cat "$f" > merged_all.csv
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.