3

Looking through the man pages for sed, awk and grep, I'm unable to find a way to search for a string that is exactly n characters long.

Given the following text file, I want to extract just 6982a9948422

ID                  IMAGE               COMMAND                
CREATED             STATUS              PORTS
6982a9948422        ubuntu:12.04        apt-get install ping   
1 minute ago        Exit 0

The value will always be in the first column and is always 12 alphanumeric characters.

Here is what I am trying so far.

 cat /tmp/test | awk {'print $1'} | sed 's/.*\([0-9],[A-Z],[a-z]\{12\}\).*//g'

What would be a way to extract just 6982a9948422 from the text above ?

  • In your case, you can use docker ps -q and avoid having to do text manipulation at all. – Dylan Frese Aug 3 '16 at 17:18
9
awk 'length($1) == 12 { print $1 }' file

The program is pretty self documenting and avoids the regex hammer.

awk -v f=1 '$f ~ /^[[:alnum:]]{12}$/ { print $f }' file

Or swing away with the above if you only want to consider first fields (fields being delimited by blanks) that consist only of alphanumeric characters.

With awk implementations that don't support the {x,y} interval regular expressions, you can change it to:

awk -v f=1 'length($f) == 12 && $f !~ /[^[:alnum:]]/ { print $f }' file
  • The problem with this is that it will pick up non-alphanumerics, too. – Joseph R. Jan 26 '14 at 17:09
  • @JosephR., that has been addressed. – Stéphane Chazelas May 11 '17 at 14:40
4

This will search for, and print, all alphanumeric strings that start at the beginning of a line and are exactly 12 characters long.

grep -o -w -E '^[[:alnum:]]{12}'

For macOS users. Install GNU grep for this to work. Can be done using homebrew.

  • 1
    This works on (GNU grep) 2.10 [ubuntu] but not on (BSD grep) 2.5.1-FreeBSD [MacOsX] – spuder Jan 26 '14 at 5:30
  • 2
    I tried to make it POSIX compliant. It works on FreeBSD 9.1. I don't have access to a Mac OS X system, sorry. – Mark Plotnick Jan 26 '14 at 6:12
  • 1
    Nothing wrong with the answer, I just wanted to clarify for any future readers who might be confused why it doesn't work for them. – spuder Jan 26 '14 at 6:28
1

You can use grep with the PCRE facility. It's available in most of the newer versions of grep.

$ grep -oP "^[[:alnum:]]{12}" test.txt
6982a9948422

This will give you all the matches that are 12 long and include characters that are valid in words, [a-zA-Z0-9].

  • This works on (GNU grep) 2.10 [ubuntu] but not on (BSD grep) 2.5.1-FreeBSD [MacOsX] – spuder Jan 26 '14 at 5:31
  • @spuder - please update your Q then, you need to be explicit if you have locked versions of OS/tools, otherwise with the generic tags you've selected the A'ers are left to guess. – slm Jan 26 '14 at 6:18
  • 1
    Nothing wrong with the answer, I just wanted to clarify for any future readers who might be confused why it doesn't work for them. – spuder Jan 26 '14 at 6:27
  • @spuder - OK, next time just make note that this is just a heads up or something, otherwise that comment might be perceived as this A doesn't solve the OP's problem. Mainly b/c it's you the OP making mention of this. 8-). Is your Q resolved then or did you want additional feedback? – slm Jan 26 '14 at 13:17
1

Another solution, using end-of-word delimiter, not apparent in any of the solutions above:

egrep '^[[:alnum:]]{12}\>' filename
  • pubs.opengroup.org/onlinepubs/007904875/basedefs/… Because it isn't standardized, thus may not work everywhere. which seems to be a untold part of the question from reading his comments. – llua Jan 26 '14 at 15:01
  • Also, careful with what \> (or \b or [[:>:]]) means where available. That's usually the transition from a word character (the definition of which varies) to a non-word character. For instance, the above will generally match on 123456789012-456 because that \> matches inbetween the 2 and - as 2 is a word character and - is not. – Stéphane Chazelas May 11 '17 at 14:35
1

AWK Solution

awk '{match( $1, /[0-9A-Za-z]{12}/, arr) }; { printf arr[0] }' inputFile

OR

awk '$1 ~ /[0-9A-Za-z]{12}/{print $1}' inputFile
0

Others have given you answers for your specific test case where the string in question is at the beginning of the line. These solutions will print all strings that consist of 12 consecutive word characters:

perl -lne 'print for /\b(\w{12})\b/g;' file
grep -oP '\b(\w{12})\b' file
0

No grep, awk, etc., just pure POSIX shell:

while read x y ; do case "$x" in ????????????) echo $x ;; esac ; done < test

Output:

6982a9948422

If the match needs to be strictly alphanumeric, there's always this:

while read x y ; do case "$x" in \
    [[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]][[:alnum:]]) echo $x ;; \
                    esac ; \
done < test
0

I would modify your own solution. Instead of deleting any $1 that doesn't match the required regexp, use grep to filter them out:

awk '{print $1}' /tmp/test | grep -iE '^[a-z0-9]{12}$'

Alternatively in pure sed:

sed 's/^\([a-zA-Z0-9]\{12\}\)[ \t]\+.*/\1/' /tmp/test     
  • That sed code does not work. This does: sed -n 's/^\([[:alnum:]]\{12\}\)[[:space:]].*/\1/p' /tmp/test. – agc Aug 4 '16 at 19:53
-2

This will process any line that has 12 alphanumeric characters and print the first field:

awk '/[[:alnum:]]{12}/ {print $1}' file

Your Answer

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.