【问题标题】:How do I extract a single chunk of bytes from within a file?如何从文件中提取单个字节块?
【发布时间】:2010-11-28 05:51:12
【问题描述】:

在 Linux 桌面 (RHEL4) 上,我想从一个大文件 (>1 Gig) 中提取一系列字节(通常小于 1000)。我知道文件的偏移量和块的大小。

我可以编写代码来执行此操作,但有命令行解决方案吗?

理想情况下,类似于:

magicprogram --offset 102567 --size 253 < input.binary > output.binary

【问题讨论】:

    标签: linux file split


    【解决方案1】:

    我遇到了同样的问题,试图剪切部分 RAW 磁盘映像。 bs=1 的 dd 不可用,因此我为该任务编写了一个简单的 C 程序。

    // usage:
    //  ./cutfile srcfile destfile offset length
    //  ./cutfile my.image movie.avi 4524 20412452
    // compile, presuming it is saved as cutfile.cc:
    //  gcc cutfile.cc -o cutfile -std=c11 -pedantic -W -Wall -Werror 
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    
    int main(int argc, char *argv[])
    {
      if(argc != 5) {
          printf("error, need 4 arguments!\n");
          return 1;
      }
    
    
      const unsigned blocksize = 16*512;  // can adjust
      unsigned char buffer[blocksize];
    
      FILE *f = fopen(argv[1], "rb");
      FILE *fout = fopen(argv[2], "wb");
      long offset = atol(argv[3]);
      long length = atol(argv[4]);
      if(f==NULL || fout==NULL) {
          perror("cannot open file");
          return 1;
      }
      fseek(f, offset, SEEK_SET);
    
      while(length > blocksize) {
          fread(buffer, 1, blocksize, f);
          fwrite(buffer, 1, blocksize, fout);
          length -= blocksize;
      }
      if(length>0) { // copy rest
          fread(buffer, 1, length, f);
          fwrite(buffer, 1, length, fout);
      }    
    
      fclose(fout);
      fclose(f);
      return 0;
    }
    

    【讨论】:

    • 请注意,在 C++ 中,您应该使用 std::ifstreamstd::ofstream...
    • 是的,但如果我将 cstdio 之类的内容更改为 stdio.h ,它实际上是纯 C 语言。我不知道为什么我选择从 C++ 头开始,也许我一开始以为我会使用更多 C++ 的东西?!
    • 是的,你可能想编辑你的答案并将其设为 C,因为 OP 询问了 C。
    【解决方案2】:

    head -c + tail -c

    不确定它与dd 在效率方面的对比如何,但它很有趣:

    printf "123456789" | tail -c+2 | head -c3
    

    选择 3 个字节,从第 2 个字节开始:

    234
    

    另见:

    【讨论】:

    • @elvis.dukaj 是的,应该没有什么不同。试试printf '\x01\x02' &gt; fhd
    • 比 bs=1 的 dd 快得多,谢谢!请注意,tail 从 1 开始计数字节,而不是从 0 开始计数。此外,当它的输出被 head 过早关闭时,tail 会以错误代码 1 退出。确保在使用“set -e”时忽略该错误。
    【解决方案3】:

    这是一个老问题,但我想添加另一个更适合大块字节的 dd 命令版本:

    dd if=input.binary of=output.binary skip=$offset count=$bytes iflag=skip_bytes,count_bytes
    

    其中$offset$bytes 是以字节为单位的数字。

    与 Thomas 接受的答案不同的是,bs=1 没有出现在这里。 bs=1 将输入和输出块大小设置为 1 字节,当要提取的字节数很大时,这会非常慢。

    这意味着我们将块大小 (bs) 保留为默认值 512 字节。使用iflag=skip_bytes,count_bytes,我们告诉ddskipcount 之后的值视为字节数量而不是块数量。

    【讨论】:

    • 这确实比我的回答快很多。
    • 不适用于 Mac - iflag 是一个未知的操作数,没有它你会得到一个完整的块。
    • @Timmmm GNU dd 可用于支持iflag (brew install coreutils)。注意:默认情况下,安装实用程序时使用 g 前缀(例如 gdd 而不是 dd
    【解决方案4】:

    试试dd:

    dd skip=102567 count=253 if=input.binary of=output.binary bs=1
    

    选项bs=1 设置块大小,使dd 一次读取和写入一个字节。默认块大小为 512 字节。

    bs 的值也会影响skipcount 的行为,因为skipcount 中的数字是dd 将跳过的块数和读/写。

    【讨论】:

    • 可选地添加status=none 以禁止输出到stderr。
    • 这里是使用十六进制偏移量的示例:dd if=in.bin bs=1 status=none skip=$((0x88)) count=$((0x80)) of=out.bin.
    • 您使用 bs=1 和 count=253 而不是相反的原因有什么具体的原因吗?更大的块大小会使命令更高效吗?
    • @rexford:跳过数也以块为单位,而不是 253 的倍数。鉴于操作系统在从文件系统上的普通文件读取时会进行自己的缓冲,在此案例效率不会像从设备读取时那样低。
    • bs=1 的加载速度比 bs=1000 慢。我实际上在一个简短的测试中看到了 500 倍。
    【解决方案5】:

    更快

    dd bs=<req len> count=1 skip=<req offset> if=input.binary of=output.binary 
    

    【讨论】:

    • 这里的问题是skip是以bs为单位的。
    • 这是 executor 的一个细节,仍然比上面的要好,确实你需要重新计算:req_offset=$(bc &lt;&lt;&lt; "$offset/$bs") 并确保它是一个整数。
    【解决方案6】:

    dd 命令可以完成所有这些。查看作为调用的一部分的 seek 和/或 skip 参数。

    【讨论】:

      猜你喜欢
      • 2021-09-06
      • 2013-07-09
      • 1970-01-01
      • 1970-01-01
      • 2013-11-27
      • 1970-01-01
      • 2012-04-19
      • 2019-05-30
      • 1970-01-01
      相关资源
      最近更新 更多