5

I am working on an enhancement of a shell script which reads a file and processes it. Basically the input file contains a header record followed by number of detail records. I want to get only the header record from the file.

$ cat sample_file.txt
header1,header2,header3,header4
value1,value2,value3,value4

The existing script uses the following command to get the header from the file:

$ cat sample_file.txt | head -1 | egrep -o '[[:print:]]' |  tr '\n' '\0'
header1,header2,header3,header4$

I am not sure what egrep -o '[[:print:]]' do here. Because even without the egrep the command could have been just put like this

To print the header as it is

$ cat sample_file.txt | head -1
header1,header2,header3,header4

Or to print the header without a new line at the end

$ cat sample_file.txt | head -1 |  tr '\n' '\0'
header1,header2,header3,header4$

The man page of egrep tells the below but it is not clear as to when [[:print:]] should be used.

Finally, certain named classes of characters are predefined within bracket expressions, as follows. Their names are self explanatory, and they are [:alnum:], [:alpha:], [:cntrl:], [:digit:], [:graph:], [:lower:], [:print:], [:punct:], [:space:], [:upper:], and [:xdigit:]. For example, [[:alnum:]] means [0-9A-Za-z], except the latter form depends upon the C locale and the ASCII character encoding, whereas the former is indepen- dent of locale and character set. (Note that the brackets in these class names are part of the symbolic names, and must be included in addition to the brackets delimiting the bracket list.) Most metacharacters lose their special meaning inside lists. To include a literal ] place it first in the list. Similarly, to include a literal ^ place it anywhere but first. Finally, to include a literal - place it last.

Can you please help me to understand the usage of egrep '[[:print:]]' option and where do we use the same.

New contributor
Shri is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
1
  • 1
    That command with tr '\n' '\0' would insert nul bytes between each character in the header. That's is most likely not what's intended.
    – they
    Oct 13 at 11:59
4

A bracket expression is something like [abc], which matches any one of the characters inside it. E.g. [abc] would match a or b, but not d or :. Character classes can be used within a bracket expression to add the whole class to the set the bracket expression matches. [[:print:]] would match a single printable character, leaving control characters, newlines and tabs out. egrep, or preferably grep -E, would print any lines that match the given pattern, and with -o, just the matching parts, one per line.

E.g. with [:alpha:], the colon is left out:

$ echo ab:c | egrep -o '[[:alpha:]]'
a
b
c

So, in effect, egrep -o '[[:print:]]' would print every printable character in the input, one per line. And then tr '\n' '\0' changes the newlines to NUL bytes, so you get all the printable characters with NULs in between. I'm not sure that makes much sense, since NULs aren't any nicer to deal with than control characters. If you open the resulting data in e.g. less or vim, you'll see the NULs printed as ^@, possibly in color.

Similarly, cat sample_file.txt | head -1 | tr '\n' '\0' doesn't remove the newline, it replaces it with a NUL.


I'm not sure what the goal here is, but to remove any newlines and tabs, you could use tr -d:

... | tr -d '\n\t'

and to remove all nonprintable characters -d with -c to complement (invert) the set of matched characters:

... | tr -dc '[:print:]'

(Note that tr doesn't take the outer set of brackets, like you need in a regex. In effect, the argument to tr is like the inside of a regex bracket expression.)

2
  • @ikkachu Thanks . I Truly agree to your point. I have removed the egrep itself from the script as it does not make sense . "So, in effect, egrep -o '[[:print:]]' would print every printable character in the input, one per line." Could you please let me know what are the printable characters and what are not the printable characters ?
    – Shri
    Oct 14 at 5:22
  • 1
    @Shri, I'm not exactly sure what the exact description is. Looking at the ASCII characters, it matches the ones that print "normally", without any special effects, i.e. the ones that have a real visible symbol: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ but also space. It doesn't match control characters (bytes 0x00 to 0x1f and 0x7f), including tab, enter, backspace, delete and Ctrl-A, Ctrl-B etc. Non-ASCII characters might depend on the locale, with Debian's en_US.UTF-8, it matches e.g. ö (obviously) but also the non-breaking space.
    – ilkkachu
    Oct 14 at 8:55
4

You can find an explanation in the GNU's Character Classes and Bracket Expressions documentation:

‘[:print:]’
Printable characters: ‘[:alnum:]’, ‘[:punct:]’, and space.


If you need the header as a whole there's no need for any parsing command like egrep, etc.


From man egrep:

In addition, the variant programs egrep, fgrep and rgrep are the same as grep -E, grep -F, and grep -r, respectively. These variants are deprecated, but are provided for backward compatibility.

So basically egrep [[:print:]] is the same as grep -E [[:print:]], which only would be needed if you need an extended regular expression (ERE), but the pattern [[:print:]] is not one of these.

Your Answer

Shri is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.