Làm thế nào để cắt bớt tập tin theo dòng?


13

Tôi có một số lượng lớn các tập tin, một số trong đó là rất dài. Tôi muốn cắt bớt chúng đến một kích thước nhất định nếu chúng lớn hơn bằng cách xóa phần cuối của tệp. Nhưng tôi chỉ muốn loại bỏ toàn bộ dòng. Tôi có thể làm cái này như thế nào? Cảm giác giống như loại điều sẽ được xử lý bởi chuỗi công cụ Linux nhưng tôi không biết lệnh đúng.

Ví dụ: giả sử tôi có tệp 120.000 byte với các dòng 300 byte và tôi đang cố gắng cắt nó thành 10.000 byte. 33 dòng đầu tiên sẽ ở lại (9900 byte) và phần còn lại sẽ bị cắt. Tôi không muốn cắt chính xác 10.000 byte, vì điều đó sẽ để lại một dòng.

Tất nhiên các tệp có độ dài khác nhau và các dòng không phải là tất cả cùng độ dài.

Lý tưởng nhất là các tệp kết quả sẽ được thực hiện ngắn hơn một chút thay vì dài hơn một chút (nếu điểm dừng nằm trên một đường dài) nhưng điều đó không quá quan trọng, nó có thể dài hơn một chút nếu điều đó dễ dàng hơn. Tôi muốn các thay đổi được thực hiện trực tiếp cho các tệp (tốt, có thể là tệp mới được sao chép ở nơi khác, bản gốc đã bị xóa và tệp mới được di chuyển, nhưng điều đó giống với POV của người dùng). Một giải pháp chuyển hướng dữ liệu đến một loạt các địa điểm và sau đó quay lại mời khả năng làm hỏng tệp và tôi muốn tránh điều đó ...


Đã xóa câu trả lời của tôi. Tôi đoán điều kích thước tệp trong Bytes không quá rõ ràng, xin lỗi. Có lẽ bạn có thể chỉnh sửa câu hỏi của mình và làm rõ phần đó (ví dụ với một ví dụ)?
slhck

@slhck: Xin lỗi khi thấy bạn mất rep chỉ vì tôi không rõ ... hãy để tôi xem tôi có thể sửa nó không.
Charles

Câu trả lời:


1

Các sed/ wcphức tạp có thể tránh được trong câu trả lời trước khi awkđược sử dụng. Sử dụng ví dụ được cung cấp từ OP (hiển thị các dòng hoàn chỉnh trước 10000 byte):

awk '{i += (length() + 1); if (i <= 10000) print $ALL}' myfile.txt

Cũng hiển thị dòng hoàn chỉnh chứa byte thứ 10000 nếu byte đó không ở cuối dòng:

awk '{i += (length() + 1); print $ALL; if (i >= 10000) exit}' myfile.txt

Câu trả lời ở trên giả định:

  1. Tệp văn bản là của terminator dòng Unix ( \n). Đối với tệp văn bản Dos / Windows ( \r\n), hãy đổi length() + 1thànhlength() + 2
  2. Tệp văn bản chỉ chứa ký tự byte đơn. Nếu có ký tự đa nhân (chẳng hạn như trong môi trường unicode), hãy đặt môi trường LC_CTYPE=Cđể buộc giải thích ở mức byte.

14

Cách sedtiếp cận là tốt, nhưng để lặp qua tất cả các dòng thì không. Nếu bạn biết bạn muốn giữ bao nhiêu dòng (để có một ví dụ, tôi sử dụng 99 ở đây), bạn có thể làm như thế này:

sed -i '100,$ d' myfile.txt

Giải thích: sedlà một bộ xử lý biểu thức chính quy. Với tùy chọn -iđược cung cấp, nó xử lý một tệp trực tiếp ("nội tuyến") - thay vì chỉ đọc nó và ghi kết quả vào đầu ra tiêu chuẩn. 100,$chỉ có nghĩa là "từ dòng 100 đến cuối tập tin" - và được theo sau bởi lệnh d, mà bạn có thể đoán đúng để viết "xóa". Vì vậy, trong ngắn hạn, lệnh có nghĩa là: "Xóa tất cả các dòng từ dòng 100 đến cuối tệp khỏi myfile.txt". 100 là dòng đầu tiên bị xóa, vì bạn muốn giữ 99 dòng.

Chỉnh sửa: Mặt khác, nếu có các tệp nhật ký mà bạn muốn giữ, ví dụ 100 dòng cuối cùng :

[ $(wc -l myfile.txt) -gt 100 ] && sed -i "1,$(($(wc -l myfile.txt|awk '{print $1}') - 100)) d" myfile.txt

Chuyện gì đang xảy ra ở đây:

  • [ $(wc -l myfile.txt) -gt 100 ]: chỉ làm như sau nếu tệp có hơn 100 dòng
  • $((100 - $(wc -l myfile.txt|awk '{print $1}'))): tính toán số dòng cần xóa (tức là tất cả các dòng của tệp ngoại trừ (cuối cùng) 100 để giữ)
  • 1, $((..)) d: xóa tất cả các dòng từ dòng đầu tiên đến dòng tính toán

EDIT: vì câu hỏi vừa được chỉnh sửa để cung cấp thêm chi tiết, tôi cũng sẽ đưa thông tin bổ sung này vào câu trả lời của mình. Thêm sự thật là:

  • một kích thước cụ thể sẽ vẫn còn với tệp (10.000 byte)
  • mỗi dòng có một kích thước cụ thể tính bằng byte (trong ví dụ 300 byte)

Từ những dữ liệu này, có thể tính toán số dòng vẫn là "/", với ví dụ này có nghĩa là 33 dòng. Thuật ngữ shell cho phép tính: $((size_to_remain / linesize))(ít nhất là trên Linux sử dụng Bash, kết quả là một số nguyên). Lệnh điều chỉnh bây giờ sẽ đọc:

# keep the start of the file (OPs question)
sed -i '34,$ d' myfile.txt
# keep the end of the file (my second example)
[ $(wc -l myfile.txt) -gt 33 ] && sed -i "1,33 d" myfile.txt

Vì các kích thước đã được biết trước, nên không còn cần phải tính toán nhúng vào sedlệnh. Nhưng để linh hoạt, bên trong một số tập lệnh shell người ta có thể sử dụng các biến.

Để xử lý có điều kiện dựa trên kích thước tệp, người ta có thể sử dụng "kiểm tra" sau đây: hướng dẫn:

[ "$(ls -lk $file | awk ' {print $5}')" -gt 100 ] &&

có nghĩa là: "nếu kích thước $filevượt quá 100kB, hãy ..." ( ls -lkliệt kê kích thước tệp trong kB ở vị trí 5, do đó awkđược sử dụng để trích xuất chính xác điều này).


OP muốn cắt tệp dựa trên một kích thước byte nhất định - không chỉ độ dài về mặt dòng. Tôi đã xóa câu trả lời của tôi liên quan head -n.
slhck

@slhck Cảm ơn bạn đã thông báo. Vâng, OP chỉ chỉnh sửa câu hỏi của mình để làm cho ý định rõ ràng hơn. Vì anh ta có nghĩa là tính toán mỗi dòng có bao nhiêu byte, câu trả lời của tôi vẫn hợp lệ về nguyên tắc - vì anh ta có thể tính toán số dòng còn lại, và sau đó sử dụng phương pháp của tôi để xử lý các tệp. Có lẽ tôi đưa ra một nhận xét ngắn về điều đó trong câu trả lời của tôi.
Izzy

Không - kích thước không được biết trước. Đó là một ví dụ. Mỗi tệp sẽ có kích thước khác nhau và các dòng có độ dài không đều. Một số tệp không cần phải cắt bớt.
Charles

Ồ, một lần nữa ... Chà, một số điều khó giải thích rõ ràng (quá nhiều khía cạnh). Đối với các tệp không cần cắt bớt, điều đó có thể dựa trên kích thước tệp? Điều đó có thể được bảo hiểm. Nhưng nếu thậm chí không có kích thước đường trung bình được biết đến, phần này trở nên khó khăn - tôi không thể nghĩ ra một giải pháp dễ dàng (không có quá nhiều chi phí) vào lúc này.
Izzy

Tất cả những gì tôi có thể đưa ra hiện tại sẽ liên quan đến việc lấy n dòng đầu tiên, tính độ dài trung bình dựa trên chúng và sử dụng giá trị này. Điều đó sẽ giúp bạn?
Izzy

0

Không tìm thấy lệnh để làm điều này, tôi đã viết một kịch bản nhanh (không được kiểm tra):

#!/bin/sh

# Usage: $0 glob.* 25000
# where glob.* is a wildcard pattern and 25000 is the maximum number of bytes.

limit=20000
tmp=/tmp/trim
[[ "$2" == +([0-9]) ]] || limit=$2
limit=`expr $len + 1`
for file in $1;
do
    [[ `wc -c $file` -lt $limit ]] && continue
    head -c $file > $tmp
    sed '$d' $tmp
    $tmp > $file
done

-1

Bạn có thể sử dụng lệnh linux sed để xóa các dòng khỏi tệp. Lệnh sau xóa dòng cuối cùng của filename.txt:

sed '$d' filename.txt

Với awk hoặc find, bạn có thể tìm kiếm mẫu phù hợp với lệnh sed của bạn. Đầu tiên bạn tìm kiếm bằng awk hoặc tìm các tệp bạn muốn rút ngắn và sau đó bạn có thể xóa các dòng bằng sed.


-1

Tôi đã làm một cái gì đó tương tự với đuôi. Để chỉ giữ 10.000 dòng cuối cùng trong trường hợp này:

TMP=$(tail -n 10000 /path/to/some/file 2>/dev/null) && echo "${TMP}" > /path/to/some/file
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.