Grep / awk trên nhiều tệp cho một đầu ra


1

Tôi có một số tệp txt chứa dữ liệu, trong đó tôi sử dụng grepđể tìm kiếm một chuỗi văn bản hiện tại và sử dụng awkđể lọc ra biến tôi cần. Chuỗi được lặp lại thông qua tệp, vì vậy tôi hiện đang sử dụng lệnh này để trích xuất chuỗi mong muốn:

grep 'text' *.txt | awk ' NR==1  {print $2 } ' > outputfile

Vấn đề là tôi muốn quay vòng qua nhiều tệp trong thư mục và với mỗi tệp, hãy lấy biến trích xuất được ghi vào một tệp đầu ra duy nhất. Tôi biết câu hỏi đã được trả lời trước đây, nhưng tôi khá mới mẻ với điều này và có một số khó khăn khi thực hiện.

Bất kỳ thông tin phản hồi sẽ được đánh giá cao!


Bạn đang chọn tất cả các tệp kết thúc trong .txtthư mục đó..như nó sẽ hoạt động nếu bạn chỉ quan tâm đến các .txttệp..Nếu không, có lẽ bạn nên đưa ra một ví dụ hoàn chỉnh ..
heemayl

1
Tôi đoán rằng bạn hiểu vấn đề của bạn một cách hoàn hảo, nhưng chúng tôi không rõ ràng. Bạn có thể vui lòng cung cấp một số ví dụ về các tệp đầu vào của bạn trông như thế nào và những gì bạn muốn nhận làm đầu ra không? Bạn nói, tôi muốn chuyển qua các tập tin của chương trình, và cho mỗi tập tin, [ghi] vào một tập tin đầu ra duy nhất mỗi tệp đầu vào . Những câu trả lời khác bạn đã xem xét; những gì bạn đã cố gắng; và làm thế nào họ đã giảm kết quả bạn muốn? Xin vui lòng không trả lời trong các ý kiến; chỉnh sửa câu hỏi của bạn để làm cho nó rõ ràng hơn.
G-Man

Câu trả lời:


1

Tôi sẽ lặp lại các tệp trong bash, giữ tên tệp của mỗi tệp, vì vậy bạn có thể chuyển hướng đầu ra thành các tệp đầu ra khác nhau cho mỗi lần lặp.

Ví dụ như thế này (không được kiểm tra):

PREFIX="/tmp/outputs"   # define where to store all the outputs
mkdir -p "${PREFIX}"    # make sure the outputs dir exists

for FILE in *.txt       # get the file names you want to work on
do
  # use ${PREFIX}/${FILE} to redirect output to a 
  # file that's associated with the input
  grep 'text' "${FILE}" | awk ' NR==1 {print $2 } ' > "${PREFIX}/${FILE}"
done

2
bạn không cần $(ls *.txt), sử dụng *.txtlà đủ. Cũng trích dẫn các biến của bạn.
123

1
@ 123 cảm ơn bạn và chúc mừng cho tên người dùng
phát lại

1

Nếu tôi hiểu chính xác, bạn muốn làm như sau cho mỗi .txttệp:

  • Xác định vị trí dòng đầu tiên chứa mẫu text.
  • Trên dòng này, lấy trường được phân tách bằng khoảng trắng thứ hai và ghi nó ra một tệp có tên liên quan đến tệp đầu vào.

Bạn không nói cách xây dựng tên tệp đầu ra. Tôi sẽ làm cho nó giống như các tập tin đầu vào, nhưng kết thúc bằng .outthay vì .txt.

Bạn có thể làm điều này với một vòng lặp shell.

for x in *.txt; do
  grep 'text' -- "$x" | awk '{print $2; exit}' >"${x%.*}.out"
done

Thoát khỏi awk ngay khi nó hoàn thành công việc của mình nhanh hơn một chút so với việc bảo nó tiếp tục đọc nhưng không làm gì cả. Một khả năng khác là bỏ qua awk hoàn toàn và để phần vỏ thực hiện phân tách dòng (việc này nhanh hơn hay chậm hơn phụ thuộc vào rất nhiều yếu tố mà tôi sẽ không dự đoán nguy hiểm):

for x in *.txt; do
  grep 'text' -- "$x" | read -r first second rest && printf '%s\n' "$rest" >"${x%.*}.out"
done

Một cách tiếp cận khác sẽ là làm tất cả công việc trong awk. Awk có thể hoạt động trên nhiều tệp và bạn có thể sử dụng chuyển hướng của awk cho đầu ra. Điều này đòi hỏi phải cắt ít quy trình hơn. Nó khá đơn giản trong Gawk (GNU awk):

awk '/text/ {print $2 >substr(FILENAME, 1, length(FILENAME)-4) ".out"; nextfile}' *.txt

Trong một triển khai awk không có nextfile, bạn cần xử lý chuyển đổi thủ công sang tệp tiếp theo, điều này làm cho cách tiếp cận này kém hấp dẫn hơn (cả phức tạp hơn và kém hiệu quả hơn).

awk '
    FNR==1 {first=1}
   first && /text/ {print $2 >substr(FILENAME, 1, length(FILENAME)-4) ".out"; first=0}' *.txt

awkPhương thức đó in từ dòng đầu tiên của mỗi tệp, không phải dòng phù hợp đầu tiên. Không gawkcần em FNR==1 {found=0} !found&&/text/ {print $2 >etc; found=1}.
dave_thndry_085
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.