【问题标题】:How to grab an arbitrary chunk from a file on Unix/Linux [duplicate]如何从 Unix/Linux 上的文件中获取任意块 [重复]
【发布时间】:2009-08-13 15:21:40
【问题描述】:

我正在尝试将一个二进制文件中的一个块复制到一个新文件中。我有我想要抓取的块的字节偏移量和长度。

我曾尝试使用dd 实用程序,但这似乎会读取并丢弃数据直到偏移量,而不仅仅是寻找(我猜是因为dd 用于复制/转换数据块)。这使得它非常慢(偏移量越高越慢)。这是我试过的命令:

dd if=inputfile ibs=1 skip=$offset count=$datalength of=outputfile

我想我可以编写一个小的 Perl/Python/任何脚本来打开文件,寻找偏移量,然后以块的形式读取和写入所需的数据量。

是否有支持此类功能的实用程序?

【问题讨论】:

  • 我刚刚尝试在 dd 上运行 strace,它使用了 llseek。
  • 啊,我在 freebsd 上,所以也许这是一个不同的实现。也许它很慢,因为我将输入缓冲区大小设置为 1 字节。

标签: bash shell unix


【解决方案1】:

您可以使用tail -c+N 修剪输入的前N 个字节,然后您可以使用head -cM 仅输出其输入的前M 个字节。

$ echo "hello world 1234567890" | tail -c+9 | head -c6
rld 12

所以使用你的变量,它可能是:

tail -c+$offset inputfile | head -c$datalength > outputfile


啊,没有看到它必须寻求。将其保留为 CW。

【讨论】:

    【解决方案2】:

    是的,今天用 dd 做这件事很尴尬。我们正在考虑将 skip_bytes 和 count_bytes 参数添加到 coreutils 中的 dd 以提供帮助。以下应该可以工作:

    #!/bin/sh
    
    bs=100000
    infile=$1
    skip=$2
    length=$3
    
    (
      dd bs=1 skip=$skip count=0
      dd bs=$bs count=$(($length / $bs))
      dd bs=$(($length % $bs)) count=1
    ) < "$infile"
    

    【讨论】:

    • 是的,添加 skip/count_bytes 会非常有用,并且让 dd 成为易于使用的通用字节抓取器 :)
    【解决方案3】:

    感谢其他答案。不幸的是,我无法安装额外的软件,所以 ddrescue 选项不可用。 head/tail 解决方案很有趣(我没有意识到您可以将 + 提供给 tail),但是扫描数据会变得很慢。

    我最终编写了一个小的 Python 脚本来做我想做的事。缓冲区大小可能应该调整为与某些外部缓冲区设置相同,但在我的系统上使用以下值就足够了。

    #!/usr/local/bin/python
    
    import sys
    
    BUFFER_SIZE = 100000
    
    # Read args
    if len(sys.argv) < 4:
        print >> sys.stderr, "Usage: %s input_file start_pos length" % (sys.argv[0],)
        sys.exit(1)
    input_filename = sys.argv[1]
    start_pos = int(sys.argv[2])
    length = int(sys.argv[3])
    
    # Open file and seek to start pos
    input = open(sys.argv[1])
    input.seek(start_pos)
    
    # Read and write data in chunks
    while length > 0:
        # Read data
        buffer = input.read(min(BUFFER_SIZE, length))
        amount_read = len(buffer)
    
        # Check for EOF
        if not amount_read:
            print >> sys.stderr, "Reached EOF, exiting..."
            sys.exit(1)
    
        # Write data
        sys.stdout.write(buffer)
        length -= amount_read
    

    【讨论】:

    • 缓冲区大小应该足够大,以保持系统调用(和上下文切换)的数量减少,并且是页面大小的倍数,以使缓存尽可能快乐。内核预读意味着它不太可能对请求的磁盘 I/O 的大小产生任何实际影响。 100000 不是 4kiB 的倍数,但从 64kiB 到 1MiB 的值是合理的。
    【解决方案4】:

    你可以使用

    --input-position=POS
    

    ddrescue 的选项。

    【讨论】:

      【解决方案5】:

      根据man dd on FreeBSD:

      skip=n

      在复制之前从输入的开头跳过 n 个块。 在支持搜索的输入上,使用 lseek(2) 操作。 否则,输入数据被读取并丢弃。对于管道, 读取正确的字节数。对于所有其他设备, 读取正确数量的块而不区分 正在读取部分或完整块。

      使用dtruss 我验证它确实在Mac OS X 上的输入文件上使用lseek()。 如果您只是认为它很慢,那么我同意这将是由于 1 字节块大小造成的评论。

      【讨论】:

        【解决方案6】:

        你可以试试 hexdump 命令:

         hexdump  -v <File Path> -c -n <No of bytes to read> -s <Start Offset> | awk '{$1=""; print $0}' | sed 's/ //g'
        

        例如)从偏移量 100 开始的“mycorefile”中读取 100 个字节。

        # hexdump  -v -c  mycorefile -n 100 -s 100 | awk '{$1=""; print $0}' | sed 's/ //g'
        \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
        \0\0\0\0001\0\0\0005\0\0\0\0020003\0
        \0\0\0\0\0\0@\0\0\0\0\0\0\0\0\0
        \0\0\0\0\0\0\0\0\0\0\0\0\0 003\0
        \0\0\0\0\0020\0\0\0\0\0\0001\0\0\0
        006\0\0\0\0020003\0\0\0\0\0\0220c\0
        \0\0\0\0
        

        然后,如果需要,使用另一个脚本将输出的所有行合并为一行。

        如果你只是想看内容:

        # /usr/bin/hexdump  -v -C  mycorefile -n 100 -s 100
        00000064  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
        00000074  00 00 00 00 01 00 00 00  05 00 00 00 00 10 03 00  |................|
        00000084  00 00 00 00 00 00 40 00  00 00 00 00 00 00 00 00  |......@.........|
        00000094  00 00 00 00 00 00 00 00  00 00 00 00 00 a0 03 00  |................|
        000000a4  00 00 00 00 00 10 00 00  00 00 00 00 01 00 00 00  |................|
        000000b4  06 00 00 00 00 10 03 00  00 00 00 00 00 90 63 00  |..............c.|
        000000c4  00 00 00 00                                       |....|
        000000c8
        #
        

        【讨论】:

          猜你喜欢
          • 2011-02-03
          • 2010-12-16
          • 2011-02-19
          • 1970-01-01
          • 2011-07-17
          • 2021-05-28
          • 2012-05-05
          • 2016-05-03
          • 1970-01-01
          相关资源
          最近更新 更多