dd vs cat-这些天dd是否仍然有用?


122

我最近意识到,我们可以使用cat多达dd,并且实际上比dd

我知道这dd在处理磁带大小实际上与正确性(而不仅仅是性能)有关的磁带时很有用。但是,在这些日子里,有些情况下dd无法做些什么cat?(在这里,我认为小于20%的性能差异无关紧要。)

具体的例子会很好!


1
有关一个具体示例,请参见此SO问题
卡姆(Camh)2011年

Answers:


156

从外观上看,它dd是IBM操作系统中的一种工具,它保留了其外观(参数传递),该工具执行一些很少使用的功能(例如EBCDIC到ASCII转换或字节序反转……如今不常见)。

曾经认为dd是用于复制大型数据块在同一磁盘上(由于更有效地利用缓存的)速度更快,但是这是不正确的,至少在目前的Linux系统。

我认为某些dd的选项在处理磁带时很有用,在这种情况下,读取实际上是按块执行的(磁带驱动程序不会像磁盘驱动程序那样将块隐藏在存储介质上)。但我不知道具体情况。

有一两件事dd可以做,不能(容易)被任何其他POSIX工具所做的是拍摄第一N字节流的。许多系统都可以使用来实现此功能head -c 42,但是head -c,尽管很常见,但POSIX中却没有(并且目前在OpenBSD等上不可用)。(tail -c是POSIX。)而且,即使head -c存在,它也可能会从源中读取太多字节(因为它在内部使用stdio缓冲),如果您从仅读取会起作用的特殊文件中进行读取,则这是一个问题。(当前的GNU coreutils使用读取确切的计数head -c,但是FreeBSD和NetBSD使用stdio。)

更一般地,dd给底层文件的API,它是众多Unix工具独特的接口:仅dd覆盖或截断文件在任何时候寻求在文件中。(这是dd的独特功能,而且是一项很大的功能;奇怪的dd是,以其他工具可以做的事情而闻名。)

  • 大多数Unix工具会覆盖其输出文件,即擦除其内容并从头开始。当您>在外壳中也使用重定向时,就会发生这种情况。
  • 您可以>>在外壳中使用重定向或通过将附加到文件的内容tee -a
  • 如果要通过删除某个点之后的所有数据来缩短文件,则底层内核和C API通过该truncate功能都支持此功能,但dd以下命令,任何命令行工具不会公开此功能:

    dd if=/dev/null of=/file/to/truncate seek=1 bs=123456  # truncate file to 123456 bytes
    
  • 同样,如果您想覆盖文件中间的数据,则可以在底层API中通过打开文件进行写入而不会截断(并lseek在必要时调用以移至所需位置)来实现,但是只能dd打开文件而不会截断或追加,或从外壳中查找更复杂的示例)。

    # zero out the second kB block in the file (i.e. bytes 1024 to 2047)
    dd if=/dev/zero of=/path/to/file bs=1024 seek=1 count=1 conv=notrunc
    

因此...作为系统工具,dd几乎没有用。作为文本(或二进制文件)处理工具,它非常有价值!


被接受,因为我认为它可以解释其他答案的要点(trunc并且seek可以从中使用dd)。
kizzx2 2011年

2
另一特殊用途:dd可以从不可搜索的文件描述符中读取二进制数据,而不会由于stdio缓冲而潜在地破坏未读取的数据。参见此处的示例:etalabs.net/sh_tricks.html
R..

2
@R ..:是的。在GNU coreutils 6.10中,head -c N调用read永远不会超过N。在NetBSD 5.1中,head -c调用是getc。在FreeBSD 7.4中,head -c调用fread
吉尔斯

1
Coreutils dd还向外壳脚本公开了O_DIRECT(等),我认为这也是独一无二的。
derobert

1
Coreutils truncate允许截断或扩展文件,从而消除了对的另一种使用dd
dcoles

22

dd命令包括cat不能容纳的很多选项。也许在您的使用情况下,cat是一种可行的替代品,但它不是dd替代品。

一个示例将dd用于复制部分内容,而不是整个内容。也许您想根据设备上的已知位置从iso映像的中间部分或硬盘驱动器的分区表中提取一些位。使用dd可以指定允许执行这些操作的开始,停止和数量选项。

这些选项dd使它对于细粒度的数据操作必不可少,而cat*只能对整个文件对象,设备或流进行操作。

*正如Gilles在评论中指出的那样,可以cat与其他工具组合以隔离某些部分,但cat仍可以对整个对象进行操作。


5
dd实际上与低级设备无关,它需要/dev像其他设备一样的条目。您可以使用复制整个分区,也可以使用复制cat一部分tail +c $(($start+1)) | head -c $count
吉尔斯

16
当然。;-)当我将一个1.6TB的磁盘映像馈入cat | head | tail以获取最后几MB时,磁盘旋转起来会把月亮吸到离地球更近的地方。
迦勒(Caleb)

2
@Gilles抱歉,我的意思是要承认我对“低级”一词的用法不是很好,尽管我指的是设备而非设备上的数据。也许“微调数据操纵”会比“操纵低水平数据”更好。
卡莱布

21

还没有人提到您可以使用dd创建稀疏文件,尽管truncate也可以将其用于相同目的。

dd if=/dev/zero of=sparse-file bs=1 count=1 seek=10GB

这几乎是即时的,并会创建一个任意的大文件,例如,该文件可用作回送文件:

loop=`losetup --show -f sparse-file`
mkfs.ext4 $loop
mkdir myloop
mount $loop myloop

令人高兴的是,它最初仅使用单个磁盘空间块,然后仅根据需要进行扩展(10GB文件的ext4格式在我的系统上消耗291 MB)。使用du看实际多少磁盘空间使用- ls仅报告了最大尺寸的文件可能会增长到。


4
ls -ls显示稀疏的大小。
2011年

2
您的命令将无用的字节写入文件。dd of=sparse-file bs=1 count=0 seek=10G相当于truncate -s 10GB sparse-file。令人困惑的是足够的,truncate并且dd有确切的相反的解释GBG...
frostschutz

5
@frostschutz:man dd说:MB =1000*1000, M =1024*1024等等。并man truncate说:MB 1000*1000, M 1024*1024,所以没有区别。我同时使用GNU coreutils ddtruncateGNU coreutils。您也应该这样做!:-)
erik

@erik:感谢您的纠正。如果最近没有更改,我必须以某种方式将其与其他内容混淆。
2013年

10

一个常见的例子是用某些东西覆盖硬盘驱动器的特定段。例如,您可能要使用以下命令删除MBR:

dd if=/dev/zero of=/dev/sda bs=446 count=1

您也可以使用它创建空文件(例如用于循环磁盘映像):

dd if=/dev/zero of=10mb.file bs=1024k count=10

顺便说一句,第二条命令是我知道用完10MB最快的方法
Kevin M

3
@Kevin:比head -c吗?请分享一个基准
吉尔斯

9

dd对于备份硬盘驱动器或其他存储设备的引导扇区(dd if=/dev/sda of=boot_sector.bin bs=512 count=1),然后在以后对其进行重写(dd if=boot_sector.bin of=/dev/sda)时非常有用。备份加密卷的标头同样有用。

cat也许可以这样做,但是在重写部分我不相信它。cat仅读取/写入一定数量的字节是困难的。


5

最近,在我的Linux发行记录中,我第一次有理由克隆了多个GB的多个分区(请参阅cf cp -ar或对我有用的rsync很多次)。当然,我转向dd'cos,每个人都知道那是您使用的...并且对此表现感到震惊。不久之后ddrescue,我使用了几次谷歌搜索功能,现在我已经使用了几次,并且效果很好(比dd快得多)。


1
ddrescue很棒,特别是对于从故障磁盘中获取数据的情况。
ryenus '16

5

这些年来,我想出了一些dd技巧。

在不友好的tty或非交互模式bash上剪切并粘贴

如果您未检测到EOF / ^ D / ^ F,则可以使用dd将文本文件传输到主机。因为它将在指定的字节数后自动停止读取。

我是在去年的一次安全演习中使用此功能的,当时我们能够在远程主机上获取非tty shell,并且需要将文件传输进来。

实际上,我什至通过base64对它们进行编码,并使用缓慢但可靠的纯bash base64解码脚本对了几个二进制文件。

dd of=textfile.txt bs=1 count=<size_of_data_in_paste_buffer>

一个超酷的技巧是,在dd运行时,如果向其发送USR1信号,它将发出其当前状态(读取的字节,每秒的字节。)

通用吞吐量状态过滤器

我编写此代码的目的是,它可以作为任何通过stdout发射数据的程序的纯bash进度过滤器。(注意:几乎所有东西都会通过stdout发出数据-对于不这样做的程序,如果不使用/ dev / stdout作为文件名,则可以欺骗它们,但实际上,每次获得X字节数,打印哈希标记(如启用哈希模式的老式FTP)

(注)进度文件是la脚的,这主要是概念的证明。如果重做,我只会使用一个变量。

 dd bs=$BLKSZ of=${TMPFILE} 2>&1 \
                | grep --line-buffered -E '[[:digit:]]* bytes' \
                | awk '{ print $1 }' >> ${PROGRESS} &

 while [[ $(pidof dd) -gt 1 ]]; do

        # PROTIP: You can sleep partial seconds
        sleep .5

        # Force dd to update us on it's progress (which gets
        # redirected to $PROGRESS file.    
        pkill -USR1 dd
        local BYTES_THIS_CYCLE=$(tail -1 $PROGRESS)
        local XFER_BLKS=$(((BYTES_THIS_CYCLE-BYTES_LAST_CYCLE)/BLKSZ))

        if [ $XFER_BLKS -gt 0 ]; then
                printf "#%0.s" $(seq 0 $XFER_BLKS)
                BYTES_LAST_CYCLE=$BYTES_THIS_CYCLE
        fi
done

使用匿名外壳文件句柄对文件进行切片

这是一个极其伪代码的示例,说明如何拥有一个签名的tar文件,该文件可以通过匿名文件句柄提供tar输入而不会出现错误,而无需使用任何tmp文件来存储部分文件数据,而不会出错。

generate_hash() {
    echo "yay!"
}

# Create a tar file, generate a hash, append it to the end
tar -cf log.tar /var/log/* 2>/dev/null
TARFILE_SIZE=$(stat -f "%z" log.tar)
SIGNATURE=$(generate_hash log.tar)
echo $SIGNATURE >>log.tar

# Then, later, extract without getting an error..

tar xvf <(dd if=$OLDPWD/log.tar bs=1 count=${TARFILE_SIZE})

tl; dr是:我发现dd非常有用。这些只是我能想到的三个例子。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.