I have a string that I want to substring in awk (or cut, or sed, whatever).

But I want to maintain the field separator.

The file has a completely random amount of junk after the field separator, of a random number of characters.



I want all three outputs to be:


Obviously the common field here is ".pgp", but a standard awk always removes the field separator.

'{sub(/.pgp.*/,""); print}'


awk -F".PGP." '{print $1}'

Is there a way to maintain the separator?

  • Yes. I intend to use regex to match all ending file types. Good addition. – Matthew O'Brian Apr 23 at 18:36
  • 1
    Why not just manually stitch the 'separator' back on? sed 's/\.pgp.*/\.pgp/' file – DopeGhoti Apr 23 at 19:47
  • 1
    Or let sed do the stitching: sed 's/\(\.pgp\).*/\1/' file – Freddy Apr 23 at 20:13

How about:

awk -F '.pgp' '{print $1 FS}'

I would be tempted to use perl -pe 's/\.pgp\K.*//' instead of awk.

for string in Dogs.pgp.123sda Dogs.pgpsjaksdasdasdaw Dogs.pgp-asasdawad2
    printf '%s --> %s\n' "$string" "${string%${string#*.???}}"


Dogs.pgp.123sda --> Dogs.pgp
Dogs.pgpsjaksdasdasdaw --> Dogs.pgp
Dogs.pgp-asasdawad2 --> Dogs.pgp

The loop iterates over the three strings. In the body of the loop, the original string is printed together with the transformed string.

The string is transformed by removing everything but the part before the first dot in the string and the three characters following the dot.

This is done by first figuring out what to remove from the original string. This is ${string#*.???}, i.e. the rest of the string after removing up to the first dot and three more characters. This is then removed from the end of the string through ${string%${string#*.???}}.

If the loop iterated over filenames matching e.g. *.pgp*, then this would also handle the cases where the filenames contained newlines. The only thing that would confuse the above transformation is if there were dots before the .pgp substring, but you could handle that by matching .pgp explicitly instead of .???.


I believe I solved the issue using a match and a substring:

'match($0, /REGEX/) {sub(/REGEX/, substr($0, RSTART, RLENGTH) ); print}'

I tried with awk substr method:

awk '{print substr($1,1,8)}' filename

The output is:


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.