คำสั่ง Linux (เช่น cat) เพื่ออ่านจำนวนอักขระที่ระบุ


120

มีคำสั่งเช่นเดียวกับcatใน linux ซึ่งสามารถส่งคืนจำนวนอักขระที่ระบุจากไฟล์ได้หรือไม่?

เช่นฉันมีไฟล์ข้อความเช่น:

Hello world
this is the second line
this is the third line

และฉันต้องการบางอย่างที่จะคืนค่าอักขระ 5 ตัวแรกซึ่งจะเป็น "สวัสดี"

ขอบคุณ


โปรดทราบว่าไม่มีคำตอบใดที่ใช้เพียง N ไบต์จากสตรีม ตัวอย่างเช่นmkfifo /tmp/test.fifo; echo "hello world">/tmp/test.fifo & head -c 5 /tmp/test.fifoยังบริโภค" world\n"ที่สูญหายไปตลอดกาล
เยติ

คำตอบ:


192

head ได้ผลเช่นกัน:

head -c 100 file  # returns the first 100 bytes in the file

.. จะแยก 100 ไบต์แรกและส่งคืน

สิ่งที่ดีในการใช้headสำหรับสิ่งนี้คือไวยากรณ์สำหรับการtailจับคู่:

tail -c 100 file  # returns the last 100 bytes in the file

คุณสามารถรวมสิ่งเหล่านี้เพื่อรับช่วงของไบต์ ตัวอย่างเช่นหากต้องการรับ100 ไบต์ที่สองจากไฟล์ให้อ่าน 200 แรกด้วยheadและใช้ tail เพื่อรับ 100 สุดท้าย:

head -c 200 file | tail -c 100

@Miffy: อ่าน 20 ไบต์แรกด้วยheadแล้วใช้tailเพื่อรับ 10 ตัวสุดท้ายเช่น:head -c 20 file | tail -c 10
Dan

47

คุณสามารถใช้ dd เพื่อแยกส่วนของไบต์โดยพลการ

ตัวอย่างเช่น,

dd skip=1234 count=5 bs=1

จะคัดลอกไบต์ 1235 ถึง 1239 จากอินพุตไปยังเอาต์พุตและทิ้งส่วนที่เหลือ

หากต้องการรับห้าไบต์แรกจากอินพุตมาตรฐานให้ทำ:

dd count=5 bs=1

โปรดทราบว่าหากคุณต้องการระบุชื่อไฟล์อินพุต dd มีการแยกวิเคราะห์อาร์กิวเมนต์แบบเก่าดังนั้นคุณต้องทำดังนี้

dd count=5 bs=1 if=filename

โปรดทราบด้วยว่า dd ประกาศอย่างเปิดเผยว่ามันทำอะไรเพื่อที่จะทิ้งสิ่งนั้นไปให้ทำ:

dd count=5 bs=1 2>&-

หรือ

dd count=5 bs=1 2>/dev/null

2
ฉันขอแนะนำให้ใช้วิธีแก้ปัญหานี้โดยทั่วไปเนื่องจากdd bs=1บังคับให้ dd อ่านและเขียนทีละอักขระซึ่งช้ากว่าheadเมื่อ count มีขนาดใหญ่มาก ไม่สามารถสังเกตได้สำหรับ count = 5 แม้ว่า
ephemient

2
แล้ว "dd count = 1 bs = 5" ล่ะ? ที่จะมีหัวอ่านห้าไบต์ในครั้งเดียว ถึงกระนั้นหัวอาจเป็นทางออกที่ชัดเจนกว่า
Ben Combee

1
ขอบคุณสำหรับสิ่งนี้ - อันที่จริงฉันกำลังมองหาวิธี 'ตัด' ไฟล์ไบนารีและddดูเหมือนว่าจะทำเคล็ดลับได้ .. ไชโย!
sdaau

นี่คือเครื่องช่วยชีวิตบน busybox โดยไม่ต้องhead -cใช้dd bs=5 count=1วิธีการนี้
Jay Paroline

11

หัว :

ชื่อ

head - เอาต์พุตส่วนแรกของไฟล์

สรุป

หัวหน้า [ OPTION ] ... [ FILE ] ...

ลักษณะ

พิมพ์ 10 บรรทัดแรกของแต่ละ FILE ไปยังเอาต์พุตมาตรฐาน ด้วยไฟล์มากกว่าหนึ่งไฟล์ให้แต่ละไฟล์นำหน้าด้วยส่วนหัวที่ให้ชื่อไฟล์ หากไม่มี FILE หรือเมื่อ FILE อยู่ - ให้อ่านอินพุตมาตรฐาน

อาร์กิวเมนต์ที่จำเป็นสำหรับตัวเลือกแบบยาวนั้นจำเป็นสำหรับตัวเลือกสั้น ๆ เช่นกัน
-c , --bytes = [-] N พิมพ์ N ไบต์แรกของแต่ละไฟล์ ด้วย "-" นำหน้าให้พิมพ์ทั้งหมดยกเว้น N ไบต์สุดท้ายของแต่ละไฟล์


3

หัวหรือหางก็ทำได้เช่นกัน:

หัว -c X

พิมพ์ X ไบต์แรก (ไม่จำเป็นต้องเป็นอักขระหากเป็นไฟล์ UTF-16) ของไฟล์ tail จะทำเช่นเดียวกันยกเว้น X ไบต์สุดท้าย

สิ่งนี้ (และตัด) เป็นแบบพกพา


3
head -Line_number file_name | tail -1 |cut -c Num_of_chars

สคริปต์นี้ให้จำนวนอักขระที่แน่นอนจากบรรทัดและตำแหน่งเฉพาะเช่น:

head -5 tst.txt | tail -1 |cut -c 5-8

ให้ตัวอักษรในบรรทัดที่ 5 และตัวอักษร 5 ถึง 8 ของบรรทัดที่ 5

หมายเหตุ : tail -1ใช้เพื่อเลือกบรรทัดสุดท้ายที่แสดงโดยส่วนหัว


2

คุณยังสามารถดึงเส้นออกแล้วตัดออกเช่น:

ชื่อไฟล์ grep 'text' | ตัด -c 1-5


สิ่งนี้จะใช้ไม่ได้หากไฟล์อินพุตเป็นสตรีมที่ไม่มีที่สิ้นสุดโดยไม่มี \ n
Ajay Brahmakshatriya

2

ฉันรู้ว่าคำตอบคือการตอบคำถามที่ถามเมื่อ 6 ปีที่แล้ว ...

แต่ฉันกำลังมองหาสิ่งที่คล้ายกันอยู่สองสามชั่วโมงแล้วพบว่า: cut -cทำอย่างนั้นพร้อมกับโบนัสเพิ่มเติมที่คุณสามารถระบุออฟเซ็ตได้

ตัด -c 1-5จะกลับมาสวัสดีและตัด -c 7-11จะกลับโลก ไม่จำเป็นต้องมีคำสั่งอื่นใด


2
คุณถูก!. ฉันแค่ต้องการเน้นความเป็นไปได้ของคำสั่งเดียวทั่วไปที่สามารถส่งคืนข้อความจากกลางไฟล์ซึ่งแตกต่างจาก head -c จะอ่านแค่อักขระเริ่มต้นส่วนหาง -c อักขระสุดท้าย และไม่ต้องใช้ grep :).
bobbyus

2

แม้ว่าจะตอบ / ยอมรับเมื่อหลายปีก่อน แต่คำตอบที่ยอมรับในปัจจุบันนั้นถูกต้องสำหรับการเข้ารหัสแบบหนึ่งไบต์ต่ออักขระเช่น iso-8859-1 หรือสำหรับชุดย่อยแบบไบต์เดี่ยวของชุดอักขระไบต์ตัวแปร (เช่นอักขระละติน ภายใน UTF-8) แม้จะใช้การต่อแบบหลายไบต์แทนก็ยังใช้ได้เฉพาะกับการเข้ารหัสแบบหลายไบต์คงที่เช่น UTF-16 เนื่องจากตอนนี้ UTF-8 อยู่ในขั้นที่จะเป็นมาตรฐานสากลได้ดีและเมื่อดูรายชื่อภาษานี้ตามจำนวนเจ้าของภาษาและรายชื่อภาษา 30 อันดับแรกตามการใช้งานของเจ้าของภาษา / รองสิ่งสำคัญคือต้องชี้ให้เห็น เทคนิคที่เป็นมิตรกับอักขระแบบไบต์แบบง่าย (ไม่ใช่ไบต์) โดยใช้cut -cและtr/ sedกับคลาสอักขระ

เปรียบเทียบสิ่งต่อไปนี้ที่ล้มเหลวเป็นสองเท่าเนื่องจากข้อผิดพลาด / ข้อสันนิษฐานที่ใช้ภาษาละตินเป็นศูนย์กลางสองประการเกี่ยวกับปัญหาไบต์กับอักขระ (หนึ่งคือheadเทียบกับcutอีกปัญหาหนึ่งคือเทียบ[a-z][A-Z]กับ[:upper:][:lower:]):

$ printf 'Πού μπορώ να μάθω σανσκριτικά;\n' | \
$     head -c 1 | \
$     sed -e 's/[A-Z]/[a-z]/g'
[[unreadable binary mess, or nothing if the terminal filtered it]]

สำหรับสิ่งนี้ (หมายเหตุ: สิ่งนี้ใช้ได้ดีกับ FreeBSD แต่ทั้งสองcut& trบน GNU / Linux ยังคงสับสนภาษากรีกใน UTF-8 สำหรับฉัน):

$ printf 'Πού μπορώ να μάθω σανσκριτικά;\n' | \
$     cut -c 1 | \
$     tr '[:upper:]' '[:lower:]'
π

อีกคำตอบล่าสุดได้เสนอ "ตัด" แล้ว แต่เป็นเพียงเพราะปัญหาด้านข้างที่สามารถใช้เพื่อระบุการชดเชยตามอำเภอใจไม่ใช่เพราะอักขระที่เกี่ยวข้องโดยตรงกับปัญหาไบต์

หากคุณcutจัดการ-cกับการเข้ารหัสไบต์ตัวแปรไม่ถูกต้องสำหรับ " Xอักขระตัวแรก" (แทนที่Xด้วยตัวเลขของคุณ) คุณสามารถลอง:

  • sed -E -e '1 s/^(.{X}).*$/\1/' -e q - ซึ่ง จำกัด ไว้ที่บรรทัดแรก
  • head -n 1 | grep -E -o '^.{X}' - ซึ่ง จำกัด ไว้ที่บรรทัดแรกและเชื่อมโยงคำสั่งสองคำสั่ง
  • dd - ซึ่งได้รับการแนะนำในคำตอบอื่น ๆ แล้ว แต่ก็ยุ่งยากจริงๆ
  • sedสคริปต์ที่ซับซ้อนพร้อมด้วยบัฟเฟอร์หน้าต่างบานเลื่อนเพื่อจัดการกับอักขระที่กระจายอยู่ในหลายบรรทัด แต่อาจยุ่งยาก / เปราะบางมากกว่าการใช้สิ่งต่างๆเช่นdd

หากคุณtrไม่จัดการคลาสอักขระที่มีการเข้ารหัสไบต์ตัวแปรอย่างถูกต้องคุณสามารถลอง:

  • sed -E -e 's/[[:upper:]]/\L&/g (GNU เฉพาะ)

ขอโทษ แต่มันใช้ไม่ได้ที่นี่ ... printf 'Πού ' | cut -c 1แค่ส่งกลับคำพูดพล่อยๆ ... มันทำตัวเหมือน 'หัว'
LEo

ตามเอกสารออนไลน์ยังไม่มี: "เลือกสำหรับการพิมพ์เฉพาะอักขระในตำแหน่งที่ระบุไว้ในรายการอักขระเช่นเดียวกับ -b ในตอนนี้ แต่ความเป็นสากลจะเปลี่ยนไป" [ gnu.org/software/coreutils/manual/html_node/…
LEo

@LEo จากลิงก์ในความคิดเห็นที่สองของคุณดูเหมือนว่าคุณกำลังใช้ระบบปฏิบัติการที่ใช้ GNU ซึ่งน่าจะเป็น GNU / Linux ดังนั้นในกรณีนี้จึงเป็นที่คาดหวัง - ฉันพูดถึงสิ่งนั้นในตอนท้ายของคำตอบของฉัน มันใช้งานได้ (& ใช้งานได้แล้ว) สำหรับฉันบน FreeBSD (และอาจอยู่ในระบบปฏิบัติการอื่น ๆ ) แต่ไม่ได้ (& ยังไม่) ทำงานบน GNU / Linux สำหรับกรณีนั้นฉันได้กล่าวถึงวิธีการอื่นในตอนท้าย ฉันเองแทบรอไม่ไหวจนกว่าจะมีคนค้นพบและอาสาหาเวลาว่างเพื่อทำความเป็นสากลที่จำเป็นสำหรับชุดเครื่องมือ GNU ในการทำงานเช่นเดียวกับคนอื่น ๆ ในแง่นั้น
rowanthorpe

0

นี่คือสคริปต์ง่ายๆที่สรุปโดยใช้ddแนวทางที่กล่าวถึงที่นี่:

extract_chars.sh

#!/usr/bin/env bash

function show_help()
{
  IT="
extracts characters X to Y from stdin or FILE
usage: X Y {FILE}

e.g. 

2 10 /tmp/it     => extract chars 2-10 from /tmp/it
EOF
  "
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi
if [ -z "$1" ]
then
  show_help
fi

FROM=$1
TO=$2
COUNT=`expr $TO - $FROM + 1`

if [ -z "$3" ]
then
  dd skip=$FROM count=$COUNT bs=1 2>/dev/null
else
  dd skip=$FROM count=$COUNT bs=1 if=$3 2>/dev/null 
fi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.