Thêm dòng vào đầu và cuối của tệp lớn


23

Tôi có kịch bản trong đó các dòng sẽ được thêm vào lúc bắt đầu và kết thúc các tệp lớn.

Tôi đã thử như hình dưới đây.

  • cho dòng đầu tiên:

    sed -i '1i\'"$FirstLine" $Filename
  • cho dòng cuối cùng:

    sed -i '$ a\'"$Lastline" $Filename  

Nhưng vấn đề với lệnh này là nó đang nối thêm dòng đầu tiên của tệp và duyệt qua toàn bộ tệp. Đối với dòng cuối cùng, nó lại đi qua toàn bộ tệp và nối thêm một dòng cuối cùng. Vì tệp rất lớn của nó (14GB) nên việc này mất nhiều thời gian.

Làm cách nào tôi có thể thêm một dòng vào đầu và một dòng khác vào cuối tệp trong khi chỉ đọc tệp một lần?

Câu trả lời:


20

sed -isử dụng tempfiles như một chi tiết triển khai, đó là những gì bạn đang gặp phải; tuy nhiên, việc chuẩn bị dữ liệu vào đầu luồng dữ liệu mà không ghi đè lên nội dung hiện có yêu cầu viết lại tệp, không có cách nào khắc phục điều đó, ngay cả khi tránh sed -i.

Nếu viết lại tệp không phải là một tùy chọn, bạn có thể xem xét thao tác với tệp khi nó được đọc, ví dụ:

{ echo some prepended text ; cat file ; } | command

Ngoài ra, sed là để chỉnh sửa luồng - một tệp không phải là luồng. Sử dụng một chương trình dành cho mục đích này, như ed hoặc ex. Các-i tùy chọn để sed là không chỉ không di động, nó cũng sẽ phá vỡ bất kỳ liên kết tượng trưng đến tập tin của bạn, vì nó về cơ bản xóa nó và tái tạo lại nó, mà là vô nghĩa.

Bạn có thể làm điều này trong một lệnh duy nhất với ednhư vậy:

ed -s file << 'EOF'
0a
prepend these lines
to the beginning
.
$a
append these lines
to the end
.
w
EOF

Lưu ý rằng tùy thuộc vào việc bạn thực hiện ed, nó có thể sử dụng tệp hoán trang, yêu cầu bạn phải có ít nhất dung lượng đó.


Xin chào, lệnh ed mà bạn cung cấp đang hoạt động rất tốt cho các tệp lớn. Nhưng tôi có 3 tệp lớn như Test, Test1, Test 2. Tôi đã đưa ra lệnh như ed -s Tes * << 'EOF' 0a trước khi bắt đầu các dòng này. $ a nối các dòng này vào cuối. w EOF Nhưng nó chỉ lấy tệp Kiểm tra và thêm dòng đầu tiên / cuối cùng. Làm thế nào chúng ta có thể thực hiện các thay đổi trong cùng một lệnh để nó phải thêm dòng đầu tiên và dòng cuối cùng trong tất cả các tệp.
UNIXbest

@UNIXbest - Sử dụng forvòng lặp:for file in Tes*; do [command]; done
Chris Down

Xin chào, tôi đã sử dụng lệnh dưới đây cho tệp trong Tes *; làm ed -s Tes * << 'EOF' 0a HEllO HDR. $ một TLR xin chào. w EOF đã hoàn thành Nhưng nó vẫn ghi vào tập tin đầu tiên.
UNIXbest

Đúng, bởi vì bạn cần sử dụng "$file", không phải Tes*là đối số để ed.
Chris Xuống

2
@UNIXbest Nếu vấn đề của bạn đã được giải quyết bằng câu trả lời này, bạn nên xem xét chấp nhận nó.
Joseph R.

9

Lưu ý rằng nếu bạn muốn tránh phân bổ toàn bộ bản sao của tệp trên đĩa, bạn có thể thực hiện:

sed '
1i\
begin
$a\
end' < file 1<> file

Điều đó sử dụng thực tế là khi stdin / stdout của nó là một tệp, sed đọc và ghi theo khối. Vì vậy, ở đây, bạn có thể ghi đè lên tệp mà nó đang đọc miễn là dòng đầu tiên bạn thêm nhỏ hơn sedkích thước khối (nên là 4k hoặc 8k).

Lưu ý rằng nếu vì một lý do nào đó sedkhông thành công (bị giết, sự cố máy ...), bạn sẽ kết thúc với một nửa tệp được xử lý, điều đó có nghĩa là một số dữ liệu kích thước của dòng đầu tiên bị thiếu ở đâu đó ở giữa.

Cũng lưu ý rằng trừ khi bạn sedlà GNU sed, điều đó sẽ không hoạt động đối với dữ liệu nhị phân (nhưng vì bạn đang sử dụng -i, nên bạn đang sử dụng GNU sed).


lỗi này đối với tôi trên Ubuntu 16.04
Csaba Toth

4

Dưới đây là một số lựa chọn (tất cả sẽ tạo một bản sao mới của tệp để đảm bảo bạn có đủ không gian cho việc đó):

  • tiếng vang / mèo đơn giản

    echo "first" > new_file; cat $File >> new_file; \
      echo "last" >> new_file; 
  • awk / gawk vv

    gawk 'BEGIN{print "first\n"}{print}END{print "last\n"}' $File > NewFile 

    awkvà ilk của nó đọc các tập tin theo từng dòng. Các BEGIN{}khối được thực hiện trước khi dòng đầu tiên và các END{}khối sau dòng cuối cùng. Vì vậy, lệnh trên có nghĩa print "first" at the beginning, then print every line in the file and print "last" at the end.

  • Perl

    perl -ne 'BEGIN{print "first\n"} print;END{print "last\n"}' $File > NewFile

    Điều này về cơ bản là giống như gawk ở trên chỉ được viết bằng Perl.


1
Lưu ý rằng trong tất cả các trường hợp này, bạn sẽ cần thêm ít nhất 14 GB dung lượng cho tệp mới.
Chris Xuống

@ChrisDown điểm tốt, tôi đã chỉnh sửa câu trả lời của mình để làm rõ điều đó. Tôi cho rằng đó không phải là vấn đề vì OP đang sử dụng sed -iđể tạo các tệp tạm thời.
terdon

3

Tôi thích đơn giản hơn nhiều:

gsed -i '1s/^/foo\n/gm; $s/$/\nbar/gm' filename.txt

Điều này biến đổi tập tin:

asdf
qwer

vào tập tin:

foo
asdf
qwer
bar

2

Bạn có thể sử dụng Vim trong chế độ Ex:

ex -sc '1i|ALFA' -c '$a|BRAVO' -cx file
  1. 1 chọn dòng đầu tiên

  2. i chèn văn bản và dòng mới

  3. $ chọn dòng cuối cùng

  4. a chắp thêm văn bản và dòng mới

  5. x lưu và đóng


Điều gì xảy ra nếu chúng ta muốn làm điều này với nhiều tập tin?
geoyws

1
@geoyws không thực sự nằm trong phạm vi của câu hỏi này
Steven Penny

bạn có chắc chắn đó là $ a chứ không phải% a?
Carlos Robles

2

Không có cách nào để chèn dữ liệu vào đầu tệp¹, tất cả những gì bạn có thể làm là tạo một tệp mới, ghi dữ liệu bổ sung và nối thêm dữ liệu cũ. Vì vậy, bạn sẽ phải viết lại toàn bộ tệp ít nhất một lần để chèn dòng đầu tiên. Tuy nhiên, bạn có thể nối dòng cuối cùng mà không cần viết lại tệp.

sed -i '1i\'"$FirstLine" $Filename
echo "$LastLine" >>$Filename

Ngoài ra, bạn có thể kết hợp hai lệnh trong một lần chạy sed.

sed -i -e '1i\'"$FirstLine" -e '$ a\'"$Lastline" $Filename

sed -itạo một tệp đầu ra mới và sau đó di chuyển nó qua tệp cũ. Điều này có nghĩa là trong khi sed đang hoạt động, có một bản sao thứ hai của tệp sử dụng hết dung lượng. Bạn có thể tránh điều này bằng cách ghi đè tệp tại chỗ , nhưng với những hạn chế lớn: dòng bạn thêm phải nhỏ hơn bộ đệm của sed và nếu hệ thống của bạn gặp sự cố, bạn sẽ bị mất tệp và một số nội dung bị mất trong giữa, vì vậy tôi mạnh mẽ đề nghị chống lại nó.

¹ Linux có cách chèn dữ liệu vào một tệp, nhưng nó chỉ có thể chèn toàn bộ số khối hệ thống tệp, nó không thể chèn các chuỗi có độ dài tùy ý. Nó hữu ích cho một số ứng dụng, chẳng hạn như cơ sở dữ liệu và máy ảo, nhưng nó vô dụng đối với các tệp văn bản.


Không đúng. Nhìn vào fallocate()FALLOC_FL_INSERT_RANGEsẵn trên XFS và ext4 trong hạt nhân hiện đại (4.xx) man7.org/linux/man-pages/man2/fallocate.2.html
Eric

@Eric Bạn chỉ có thể chèn toàn bộ các khối, tuy nhiên, không phải độ dài byte tùy ý, ít nhất là từ Linux 4.15.0 với ext4. Có một hệ thống tập tin có thể chèn độ dài byte tùy ý?
Gilles 'SO- ngừng trở nên xấu xa'

Đúng nhưng nó vẫn không làm cho tuyên bố của bạn chính xác. Bạn đã viết: "Không có cách nào để chèn dữ liệu vào đầu tệp". Điều đó vẫn không đúng: có một cơ chế chèn phạm vi ở đầu tệp. Nó đi kèm với sự cẩn thận, chắc chắn, nhưng nó đáng được đề cập bởi vì một số người dùng có thể không quan tâm đến các hạn chế kích thước khối bằng cách lấp đầy khoảng trống hoặc trả lại vận chuyển.
Eric

0
$ (echo "Some Text" ; cat file1) > file2

4
Chỉ có câu trả lời mã không được chấp nhận, vui lòng cải thiện câu trả lời của bạn
Networker

Xem xét mở rộng câu trả lời của bạn để bao gồm giải thích về đề xuất của bạn hoặc liên kết đến tài liệu hỗ trợ giải pháp của bạn.
HalosGhost

-1

Các hạt nhân Linux hiện đại (cao hơn 4.1 hoặc 4.2) hỗ trợ chèn dữ liệu vào đầu tệp thông qua fallocate() gọi hệ thống với hệ thốngFALLOC_FL_INSERT_RANGE ext4 và xfs. Về bản chất, đây là một hoạt động dịch chuyển hợp lý: dữ liệu được di chuyển một cách hợp lý ở mức bù cao hơn.

Một ràng buộc tồn tại liên quan đến độ chi tiết của phạm vi bạn muốn chèn vào đầu tệp. Nhưng đối với các tệp văn bản, bạn có thể có thể phân bổ nhiều hơn một chút so với yêu cầu (tối đa đến ranh giới chi tiết) và điền vào chỗ trống hoặc trả về vận chuyển, nhưng điều đó phụ thuộc vào ứng dụng của bạn

Tôi không biết về bất kỳ tiện ích linux có sẵn nào có thể thao túng phạm vi tệp nhưng không khó để viết: lấy một mô tả tệp và gọi fallocate()với các đối số thích hợp. Để biết thêm chi tiết, tham khảo trang man của fallocatecuộc gọi hệ thống: http://man7.org/linux/man-pages/man2/fallocate.2.html


Một tiện ích không phải là vấn đề (giả sử Linux không được nhúng): produc-linux chứa một fallocatetiện ích. Vấn đề là độ chi tiết của toàn bộ các khối làm cho điều này trở nên vô dụng đối với hầu hết các tệp văn bản. Một vấn đề khác là phân bổ phạm vi và sửa đổi tiếp theo không phải là nguyên tử. Vì vậy, điều này không thực sự giải quyết vấn đề ở đây.
Gilles 'SO- ngừng trở nên xấu xa'

Độ chi tiết là một cảnh báo mà tôi đã đề cập và không, nó không làm cho nó vô dụng, nó phụ thuộc vào ứng dụng. Nơi mà bạn đã thấy trong câu hỏi rằng nguyên tử là quan trọng? Tôi chỉ có thể thấy vấn đề của buổi biểu diễn. Mặc dù vậy, tòa nhà này dường như là nguyên tử: elixir.bootlin.com/linux/latest/source/fs/open.c#L228 và nếu nguyên tử trở nên quan trọng (không phải vậy, nhưng hãy nói rằng đó là vì lý lẽ) chỉ cần sử dụng khóa tập tin. (chỉ cho tôi đến vị trí trong mã hạt nhân nơi fallocatenguyên tử bị phá vỡ, tôi tò mò)
Eric
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.