Cách tốt nhất để loại bỏ byte từ đầu tập tin?


61

Hôm nay tôi đã phải loại bỏ 1131 byte đầu tiên khỏi tệp nhị phân / văn bản hỗn hợp 800 MB, một kết xuất lật đổ được lọc mà tôi đang hack cho một kho lưu trữ mới. Cách tốt nhất để làm điều này là gì?

Để bắt đầu với tôi đã cố gắng

dd bs=1 skip=1131 if=filtered.dump of=trimmed.dump

nhưng sau khi bỏ qua điều này sẽ sao chép phần còn lại của tệp một byte tại một thời điểm, tức là rất chậm. Cuối cùng, tôi đã tìm ra 405 byte để làm tròn số này lên đến ba khối 512 mà tôi có thể bỏ qua

dd if=/dev/zero of=405zeros bs=1 count=405
cat 405zeros filtered.dump | dd bs=512 skip=3 of=trimmed.dump

Hoàn thành khá nhanh nhưng phải có cách đơn giản / tốt hơn? Có công cụ nào khác mà tôi đã quên không? Cảm ơn!


ddlà công cụ phù hợp cho công việc - có vẻ như bạn đã đưa ra một giải pháp hay, thanh lịch cho vấn đề của mình.
Justin Ethier

Câu trả lời:


62

Bạn có thể chuyển đổi bs và bỏ qua các tùy chọn:

dd bs=1131 skip=1 if=filtered.dump of=trimmed.dump

Bằng cách này, hoạt động có thể được hưởng lợi từ một khối lớn hơn.

Mặt khác, bạn có thể thử với đuôi (mặc dù không an toàn khi sử dụng tệp nhị phân):

tail -c +1132 filtered.dump >trimmed.dump

Cuối cùng, bạn có thể sử dụng 3 trường hợp dd để viết một cái gì đó như thế này:

dd if=filtered.dump bs=512k | { dd bs=1131 count=1 of=/dev/null; dd bs=512k of=trimmed.dump; }

trong đó dd đầu tiên in đầu ra tiêu chuẩn của nó được lọc.dump; cái thứ hai chỉ đọc 1131 byte và ném chúng đi; sau đó, cái cuối cùng đọc từ đầu vào tiêu chuẩn của nó các byte còn lại của filtered.dump và ghi chúng vào trimmed.dump.


6
Cảm ơn! Tôi không biết rằng đầu vào đường ống được chuyển sang quy trình thứ hai như thế - điều đó rất gọn gàng. Tôi không thể tin rằng tôi đã không nghĩ về bs=1131 skip=1mặc dù: - /
Rup

2
Hầu hết các triển khai hiện đại của tiện ích shell hoạt động chính xác với các tệp nhị phân (nghĩa là chúng không gặp rắc rối với các ký tự null và sẽ không chèn thêm một dòng mới vào cuối tệp). Chắc chắn việc triển khai GNU và * BSD là an toàn.
Gilles 'SO- ngừng trở nên xấu xa'

"Không an toàn để sử dụng nó với các tệp nhị phân" nghĩa là gì?
Scott

17

Không chắc chắn khi skip_bytesđược thêm, nhưng để bỏ qua 11 byte đầu tiên bạn có:

# echo {123456789}-abcdefgh- | 
                              dd bs=4096 skip=11 iflag=skip_bytes
-abcdefgh-
0+1 records in
0+1 records out
11 bytes (11 B) copied, 6.963e-05 s, 158 kB/s

Trường hợp iflag=skip_bytesyêu cầu dd diễn giải giá trị cho skiptùy chọn dưới dạng byte thay vì khối, làm cho nó đơn giản.


Chắc chắn là một lợi thế tốc độ cho các tệp lớn và một lượng nhỏ dữ liệu sẽ bị xóa.
sstn

Đây là câu trả lời tốt nhất, vì nó hoạt động cho mỗi kích thước khối ví dụiflag=skip_bytes skip=1234 bs=1M
phiresky

15

Bạn có thể sử dụng một vỏ con và hai ddcuộc gọi như thế này:

$ ( dd bs=1131 count=1 of=dev_null && dd bs=4K of=out.mp3 ) < 100827_MR029_LobbyControl.mp3
1+0 records in
1+0 records out
1131 bytes (1.1 kB) copied, 7.9691e-05 s, 14.2 MB/s
22433+1 records in
22433+1 records out
91886130 bytes (92 MB) copied, 0.329823 s, 279 MB/s
$ ls -l *
-rw------- 1 max users 91887261 2011-02-03 22:59 100827_MR029_LobbyControl.mp3
-rw-r--r-- 1 max users     1131 2011-02-03 23:04 dev_null
-rw-r--r-- 1 max users 91886130 2011-02-03 23:04 out.mp3
$ cat dev_null out.mp3 > orig
$ cmp 100827_MR029_LobbyControl.mp3 orig

1
Cảm ơn - Tôi không biết đầu vào đường ống tiếp tục quá trình thứ hai như vậy, tôi đoán đó là vỏ phụ? Tôi chắc chắn sẽ nhớ điều đó! Tôi đã cho Marco đánh dấu vì anh ấy đã đến đây trước nhưng +1 và cảm ơn vì câu trả lời!
Rupi

1
@Rup, vâng, shell con - được tạo thông qua dấu ngoặc đơn - cung cấp một bộ mô tả tệp stdin và cả hai lệnh gọi dd liên tiếp tiêu thụ đầu vào từ nó. Vâng - Marco đã đánh bại tôi sau 29 giây :)
maxschlepzig

6

Nếu hệ thống tập tin và nhân Linux hỗ trợ thì bạn có thể thử fallocatenếu bạn muốn thực hiện các thay đổi tại chỗ: trong trường hợp tốt nhất không có dữ liệu IO nào cả:

$ fallocate <magic> -o 0 -l 1131 inplace.dump

trong đó <magic>phụ thuộc vào hệ thống tệp, phiên bản Linux và loại tệp ( FALLOC_FL_COLLAPSE_RANGEhoặc FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEcó thể được sử dụng nội bộ ).


1
Đây là phương pháp ưa thích của tôi, nhưng chạy nó trong một container có vấn đề của nó. stackoverflow.com/questions/31155591/ từ
michaelcurry

3

Bạn nên sử dụng count=0- đó là một cách đơn giản lseek()bất cứ khi nào có thể.

Như thế này:

{  dd bs=1131 skip=1 count=0; cat; } <filtered.dump >trimmed.dump

ddsẽ lseek()mô tả tệp đầu vào thành phần bù 1131 byte, và sau đó catsẽ chỉ sao chép bất cứ thứ gì còn lại vào đầu ra.


2

Tuy nhiên, một cách khác để loại bỏ các byte hàng đầu khỏi một tệp (hoàn toàn không sử dụng dd) là sử dụng xxdsedhoặc tailtương ứng.

bytes=$((1131*2))

xxd -p -c 256 filtered.dump | tr -d '\n' | sed "s/^.\{0,${bytes}\}//" | xxd -r -p > trimmed.dump

bytes=$((bytes + 1)) 
xxd -p -c 256 filtered.dump | tr -d '\n' | tail -c +${bytes} | xxd -r -p > trimmed.dump

Điều đó gọn gàng, nhưng tôi nghĩ rằng tôi thích chỉ làm việc với tệp ở dạng nhị phân hơn là chuyển đổi nó thành và từ hex.
Rupi

2

@maxschlepzig yêu cầu một lớp lót trực tuyến. Đây là một trong perl. Phải mất 2 đối số: Từ byte và chiều dài. Tệp đầu vào phải được cung cấp bởi '<' và đầu ra sẽ ở trên thiết bị xuất chuẩn:

perl -e 'sysseek(STDIN,shift,0) || die; $left = shift;
     while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){
        $left -= $read; syswrite(STDOUT,$buf);
     }' 12345678901 19876543212 < bigfile > outfile

Nếu độ dài lớn hơn tệp, phần còn lại của tệp sẽ được sao chép.

Trên hệ thống của tôi, điều này cung cấp 3,5 GB / s.


Tôi nghĩ rằng thách thức một dòng của anh ấy là khiến bạn chứng minh rằng giải pháp ngôn ngữ kịch bản tốt hơn giải pháp vỏ một dòng của anh ấy. Và tôi thích anh ấy hơn: nó ngắn hơn và rõ ràng hơn với tôi. Nếu bạn hoạt động tốt hơn thì đó là vì bạn đang sử dụng kích thước khối lớn hơn anh ta, điều này cũng dễ dàng được nâng cấp trong phiên bản của anh ta.
Rupi

@Rup Than ôi, nhưng không. Bạn dường như quên rằng ddkhông đảm bảo đọc đầy đủ. Hãy thử: có | dd bs = 1024k đếm = 10 | wc unix.stackexchange.com/questions/17295/ từ
Ole Tange

Ngoài ra, giải pháp của tôi sẽ không đọc các byte bạn không cần (có thể dài vài terabyte).
Ole Tange
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.