dd vs cat - dd ยังคงเกี่ยวข้องกันหรือไม่?


122

ฉันเพิ่งตระหนักว่าเราสามารถใช้งานได้catมากddและเร็วกว่าจริงdd

ฉันรู้ว่าddมันมีประโยชน์ในการจัดการกับเทปที่ขนาดของบล็อกมีความสำคัญในความถูกต้องไม่ใช่แค่ประสิทธิภาพ แต่ในวันนี้มีสถานการณ์ที่ddสามารถทำอะไรบางอย่างที่catไม่สามารถ? (ที่นี่ฉันจะคำนึงถึงความแตกต่างด้านประสิทธิภาพที่น้อยกว่า 20% ที่ไม่เกี่ยวข้อง)

ตัวอย่างคอนกรีตจะดี!


1
ดูคำถาม SO นี้สำหรับตัวอย่างที่เป็นรูปธรรมหนึ่งตัวอย่าง
camh

คำตอบ:


156

ในลักษณะที่ปรากฏddเป็นเครื่องมือจากระบบปฏิบัติการของ IBM ที่ยังคงรักษารูปลักษณ์ภายนอก (ผ่านพารามิเตอร์) ซึ่งดำเนินการฟังก์ชั่นที่ไม่ค่อยได้ใช้ (เช่น EBCDIC ไปเป็น ASCII การแปลงหรือการกลับรายการ endianness ... ไม่เป็นที่ต้องการในปัจจุบัน)

ฉันเคยคิดว่าddมันเร็วกว่าสำหรับการคัดลอกบล็อกข้อมูลขนาดใหญ่บนดิสก์เดียวกัน (เนื่องจากการใช้บัฟเฟอร์อย่างมีประสิทธิภาพมากขึ้น) แต่นี่ไม่เป็นความจริงอย่างน้อยในระบบ Linux ในปัจจุบัน

ฉันคิดว่าddตัวเลือกบางอย่างมีประโยชน์เมื่อต้องจัดการกับเทปซึ่งการอ่านจะดำเนินการในบล็อกจริงๆ (ไดรเวอร์เทปไม่ซ่อนบล็อกบนสื่อเก็บข้อมูลในลักษณะที่ดิสก์ไดรเวอร์ทำ) แต่ฉันไม่ทราบรายละเอียดเฉพาะ

สิ่งหนึ่งที่ddสามารถทำได้ที่เครื่องมือ POSIX อื่นไม่สามารถทำได้อย่างง่ายดายคือการใช้ N ไบต์แรกของสตรีม ระบบจำนวนมากสามารถทำได้ด้วยhead -c 42แต่head -cในขณะที่ทั่วไปไม่ได้อยู่ใน POSIX (และไม่สามารถใช้ได้ในวันนี้ใน OpenBSD) ( tail -cคือ POSIX.) นอกจากนี้แม้จะhead -cมีอยู่ก็อาจอ่านไบต์จำนวนมากเกินไปจากแหล่งที่มา (เพราะใช้ stdio บัฟเฟอร์ภายใน) ซึ่งเป็นปัญหาหากคุณกำลังอ่านจากไฟล์พิเศษที่เพิ่งอ่านมีผล (coreutils GNU ปัจจุบันอ่านจำนวนที่แน่นอนด้วย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
    
  • หากคุณต้องการเขียนทับข้อมูลที่อยู่ตรงกลางของไฟล์อีกครั้งสิ่งนี้เป็นไปได้ใน underyling 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

2
การใช้งานพิเศษอีกหนึ่งอย่าง: ddสามารถอ่านข้อมูลไบนารีจากตัวอธิบายไฟล์ที่ไม่สามารถมองเห็นได้โดยไม่ทำลายข้อมูลที่ยังไม่ได้อ่านเนื่องจากบัฟเฟอร์ stdio ดูตัวอย่างได้ที่นี่: etalabs.net/sh_tricks.html
R ..

2
@R .. : ใช่ ใน GNU coreutils 6.10, head -c Nสายreadและไม่เคยไปเกินเอ็นใน NetBSD 5.1 สายhead -c getcใน FreeBSD 7.4 เรียกhead -c fread
Gilles

1
Coreutils ddยังตีแผ่ O_DIRECT (ฯลฯ ) ให้กับการเขียนสคริปต์เชลล์ซึ่งฉันคิดว่ามันก็ไม่เหมือนกัน
Derobert

1
coreutils ช่วยให้ตัดทอนหรือขยายไฟล์จึงช่วยลดการใช้งานของผู้อื่นtruncate dd
dcoles

22

ddคำสั่งรวมถึงจำนวนมากของตัวเลือกที่แมวจะไม่สามารถที่จะรองรับ บางทีในกรณีที่การใช้งานของคุณ cat เป็นสิ่งทดแทนที่ใช้การได้ แต่ไม่ใช่การทดแทนวันที่

ตัวอย่างหนึ่งจะใช้ddเพื่อคัดลอกส่วนหนึ่งของบางสิ่ง แต่ไม่ใช่ทั้งหมด บางทีคุณอาจต้องการดึงบิตบางส่วนออกจากตรงกลางของอิมเมจ iso หรือตารางพาร์ติชันจากฮาร์ดไดรฟ์ตามตำแหน่งที่ทราบบนอุปกรณ์ ด้วยddคุณสามารถระบุตัวเลือกการเริ่มหยุดและปริมาณที่อนุญาตการกระทำเหล่านี้

ตัวเลือกเหล่านี้ddทำให้ไม่จำเป็นสำหรับการจัดการข้อมูลที่ละเอียดยิ่งขึ้นในขณะที่cat* สามารถทำงานกับวัตถุไฟล์ทั้งหมดอุปกรณ์หรือสตรีมได้

* ตามที่ระบุไว้โดย Gilles ในความคิดเห็นเป็นไปได้ที่จะรวมcatกับเครื่องมืออื่น ๆ เพื่อแยกบางส่วนของบางสิ่ง แต่catยังคงทำงานกับวัตถุทั้งหมด


5
ddอันที่จริงไม่มีอะไรเกี่ยวข้องกับอุปกรณ์ระดับต่ำ แต่ต้องการรายการที่/devเหมือนกัน คุณสามารถคัดลอกพาร์ทิชันทั้งที่มีหรือเป็นส่วนหนึ่งของมันด้วยcat tail +c $(($start+1)) | head -c $count
Gilles

16
แน่นอน. ;-) และเมื่อฉันป้อนอิมเมจดิสก์ 1.6TB cat | head | tailเพื่อดึงข้อมูลไม่กี่ MB ล่าสุดดิสก์ที่หมุนขึ้นจะดูดดวงจันทร์เข้ามาใกล้โลกมากขึ้น
Caleb

2
@Gilles ขออภัยฉันหมายความว่ายอมรับว่าการใช้คำว่า "ระดับต่ำ" ของฉันนั้นไม่ค่อยดีมากนักแม้ว่าฉันจะอ้างถึงข้อมูลบนอุปกรณ์ไม่ใช่อุปกรณ์ก็ตาม บางที "การปรับแต่งการจัดการข้อมูลที่ละเอียด" อาจดีกว่า "การจัดการข้อมูลระดับต่ำ"
Caleb

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

สิ่งที่ดีคือในตอนแรกมันใช้เพียงแค่ดิสก์บล็อกเดียวและหลังจากนั้นก็จะเพิ่มขึ้นตามความจำเป็นเท่านั้น (การจัดรูปแบบ ext4 ของไฟล์ 10GB กิน 291 MB บนระบบของฉัน) ใช้duเพื่อดูจำนวนเนื้อที่ดิสก์ที่ใช้จริง - lsรายงานเฉพาะขนาดสูงสุดที่ไฟล์อาจเติบโต


4
ls -lsแสดงให้คุณเห็นขนาดเบาบาง
jmtd

2
คำสั่งของคุณเขียนไบต์ไร้ประโยชน์ไปยังไฟล์ จะเทียบเท่ากับdd of=sparse-file bs=1 count=0 seek=10G truncate -s 10GB sparse-fileมีความสับสนtruncateและddมีการตีความตรงข้ามGBกับG...
frostschutz

5
@frostschutz: man ddพูดว่า: MB =1000*1000, M =1024*1024และอื่น ๆ และman truncateพูดว่า: MB 1000*1000, M 1024*1024ดังนั้นจึงไม่มีความแตกต่าง ผมใช้ทั้งสองddและtruncateจาก coreutils GNU คุณควรทำเช่นกัน! :-)
erik

@erik: ขอบคุณสำหรับการแก้ไข ถ้ามันไม่เปลี่ยนไปเมื่อเร็ว ๆ นี้ฉันต้องสับสนกับสิ่งอื่น
frostschutz

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
@ เควิน: เร็วกว่าhead -cหรือ กรุณาแบ่งปันมาตรฐาน !
Gilles

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

เมื่อไม่นานมานี้ฉันได้มีการโคลนพาร์ติชั่นหลายร้อย GB เป็นครั้งแรกในประวัติศาสตร์ linuxing ของฉัน (cf cp -arหรือrsyncที่ให้บริการฉันหลายครั้ง) แน่นอนฉันหันไปdd'เพราะทุกคนรู้ว่านั่นคือสิ่งที่คุณใช้ ... และได้รับการตื่นตระหนกจากการแสดง googling เล็กน้อยนำฉันไปสู่ddrescueสิ่งที่ฉันได้ใช้ไม่กี่ครั้งในขณะนี้และทำงานได้ดีอย่างยอดเยี่ยม (เร็วกว่า dd)


1
ddrescueดีมากโดยเฉพาะการดึงข้อมูลออกมาจากดิสก์ที่ล้มเหลว
ryenus

5

นี่คือเทคนิคบางอย่างที่ฉันได้ทำมานานหลายปี ..

ตัดและวางเพื่อทุบตีโหมด tty ที่ไม่เป็นมิตรหรือไม่โต้ตอบ

หากคุณอยู่ในสถานการณ์ที่ตรวจไม่พบ EOF / ^ D / ^ F คุณสามารถใช้ dd เพื่อถ่ายโอนไฟล์ข้อความไปยังโฮสต์ เนื่องจากมันจะหยุดอ่านหลังจากจำนวนไบต์ที่ระบุโดยอัตโนมัติ

ฉันใช้สิ่งนี้เมื่อเร็ว ๆ นี้เมื่อปีที่แล้วในระหว่างการฝึกความปลอดภัยซึ่งเราสามารถรับเชลล์ที่ไม่ใช่ tty บนรีโมตโฮสต์และต้องการถ่ายโอนไฟล์

ในความเป็นจริงฉันยังได้ไบนารีไฟล์สองไฟล์โดยการเข้ารหัส base64 และใช้สคริปต์การถอดรหัส pure-bash base64 แบบช้า แต่เชื่อถือได้

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

เคล็ดลับที่เด็ดสุด ๆ คือขณะที่ dd กำลังทำงานหากคุณส่งสัญญาณ USR1 มันจะปล่อยสถานะปัจจุบัน (อ่านไบต์ไบต์ต่อวินาที .. )

ตัวกรองสถานะปริมาณงานทั่วไป

ฉันเขียนสิ่งนี้เพื่อทำหน้าที่เป็นตัวกรองความคืบหน้าของ bash บริสุทธิ์สำหรับโปรแกรมใด ๆ ที่ปล่อยข้อมูลผ่าน stdout (หมายเหตุ: ทุกอย่างจะปล่อยข้อมูลผ่าน stdout มาก - สำหรับโปรแกรมที่ไม่สามารถโกงได้หากพวกเขาไม่สร้างปัญหาให้คุณโดยใช้ / dev / stdout เป็นชื่อไฟล์ แต่ความคิดนั้นโดยทั่วไปทุกครั้งที่คุณได้ X จำนวนไบต์, พิมพ์เครื่องหมายแฮช (เช่น FTP โรงเรียนเก่าเมื่อคุณเปิดโหมดแฮช)

(หมายเหตุ) สิ่งที่ไฟล์ความคืบหน้าเป็นง่อยนี่เป็นส่วนใหญ่พิสูจน์แนวคิด ถ้าฉันเปลี่ยนมันฉันก็แค่ใช้ตัวแปร

 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

ไฟล์ slice-and-dice โดยใช้เชลล์ filehandles ที่ไม่ระบุชื่อ

นี่คือตัวอย่างรหัสเทียมอย่างยิ่งเกี่ยวกับวิธีที่คุณสามารถมีไฟล์ tar ที่ลงชื่อแล้วซึ่งคุณสามารถแตกไฟล์โดยไม่มีข้อผิดพลาดโดยให้ข้อมูลอินพุต tar ผ่าน filehandle แบบไม่ระบุชื่อโดยไม่ใช้ไฟล์ 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 มีประโยชน์อย่างไม่น่าเชื่อ และนี่เป็นเพียงสามตัวอย่างที่ฉันสามารถนึกได้จากส่วนบนของหัว


4

คุณสามารถเปลี่ยนเส้นทางเนื้อหาที่ส่งออกบางส่วน มีประโยชน์อย่างยิ่งหากคุณจำเป็นต้องเขียนด้วยsudo:

echo some_content | sudo dd status=none of=output.txt

นอกจากนี้sudoมันเทียบเท่ากับ:

echo some_content > output.txt

หรือสิ่งนี้:

echo some_content | sudo tee output.txt > /dev/null

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.