Linux command (like cat) to read a specified quantity of characters

0 votes
asked Oct 20, 2008 by pbreault

Is there a command like cat in linux which can return a specified quantity of characters from a file?

e.g., I have a text file like:

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

And I want something that would return the first 5 characters, which would be "hello".

thanks

8 Answers

0 votes
answered Oct 20, 2008 by gimel

head:

Name

head - output the first part of files

Synopsis

head [OPTION]... [FILE]...

Description

Print the first 10 lines of each FILE to standard output. With more than one FILE, precede each with a header giving the file name. With no FILE, or when FILE is -, read standard input.

Mandatory arguments to long options are mandatory for short options too.
-c, --bytes=[-]N print the first N bytes of each file; with the leading '-', print all but the last N bytes of each file

0 votes
answered Oct 20, 2008 by zathrus

head or tail can do it as well:

head -c X

Prints the first X bytes (not necessarily characters if it's a UTF-16 file) of the file. tail will do the same, except for the last X bytes.

This (and cut) are portable.

0 votes
answered Oct 20, 2008 by dan

head works too:

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

..will extract the first 100 bytes and return them.

What's nice about using head for this is that the syntax for tail matches:

tail -c 100 file  # returns the last 100 bytes in the file
0 votes
answered Oct 20, 2008 by nkr1pt

you could also grep the line out and then cut it like for instance:

grep 'text' filename | cut -c 1-5

0 votes
answered Oct 20, 2008 by fcw

You can use dd to extract arbitrary chunks of bytes.

For example,

dd skip=1234 count=5 bs=1

would copy bytes 1235 to 1239 from its input to its output, and discard the rest.

To just get the first five bytes from standard input, do:

dd count=5 bs=1

Note that, if you want to specify the input file name, dd has old-fashioned argument parsing, so you would do:

dd count=5 bs=1 if=filename

Note also that dd verbosely announces what it did, so to toss that away, do:

dd count=5 bs=1 2>&-

or

dd count=5 bs=1 2>/dev/null
0 votes
answered Oct 23, 2014 by bobbyus

I know the answer is in reply to a question asked 6 years ago ...

But I was looking for something similar for a few hours and then found out that: cut -c does exactly that, with an added bonus that you could also specify an offset.

cut -c 1-5 will return Hello and cut -c 7-11 will return world. No need for any other command

0 votes
answered Oct 1, 2016 by rowan-thorpe

Even though this was answered/accepted years ago, the presently accepted answer is only correct for one-byte-per-character encodings like iso-8859-1, or for the single-byte subsets of variable-byte character sets (like Latin characters within UTF-8). Even using multiple-byte splices instead would still only work for fixed-multibyte encodings like UTF-16. Given that now UTF-8 is well on its way to being a universal standard, and when looking at this list of languages by number of native speakers and this list of top 30 languages by native/secondary usage, it is important to point out a simple variable-byte character-friendly (not byte-based) technique, using cut -c and tr/sed with character-classes.

Compare the following which doubly fails due to two common Latin-centric mistakes/presumptions regarding the bytes vs. characters issue (one is head vs. cut, the other is [a-z][A-Z] vs. [: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]]

to this (note: this worked fine on FreeBSD, but both cut & tr on GNU/Linux still mangled Greek in UTF-8 for me though):

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

Another more recent answer had already proposed "cut", but only because of the side issue that it can be used to specify arbitrary offsets, not because of the directly relevant character vs. bytes issue.

If your cut doesn't handle -c with variable-byte encodings correctly, for "the first X characters" (replace X with your number) you could try:

  • sed -E -e '1 s/^(.{X}).*$/\1/' -e q - which is limited to the first line though
  • head -n 1 | grep -E -o '^.{X}' - which is limited to the first line and chains two commands though
  • dd - which has already been suggested in other answers, but is really cumbersome
  • A complicated sed script with sliding window buffer to handle characters spread over multiple lines, but that is probably more cumbersome/fragile than just using something like dd

If your tr doesn't handle character-classes with variable-byte encodings correctly you could try:

  • sed -E -e 's/[[:upper:]]/\L&/g (GNU-specific)
0 votes
answered Sep 15, 2017 by brad-parks

Here's a simple script that wraps up using the dd approach mentioned here:

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
Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...