Printing field that also contains FS character?

Viewed 153

The record to process in AWK has these possible formats:

foobar is fixed length, serialno is variable length, and the field I want to capture may contain zero or more underscores.

foobar_823932230_processname.txt
foobar_82393280_process_name.txt
foobar_8239330_foo_process_name.txt

Desired output

processname
process_name
foo_process_name

If I use FS="[_.]" then I can print $3 which works for first record but not 2nd and 3rd.

How can I capture everything between the serial number and .txt?

I'm editing legacy AWK code that needs changing. Once I correctly capture this field, then the awk process continues to generate additional output.

5 Answers

This cut + cut should also work:

cut -d_ -f3- file | cut -d. -f1
processname
process_name
foo_process_name

An awk solution would be using this regex:

awk '{gsub(/^([^_]+_){2}|\..*$/, "")} 1' file

With a sed that has -E to enable EREs (e.g. GNU sed and BSD/OSX sed):

$ sed -E 's/([^_]+_){2}(.*)\.txt$/\2/' file
processname
process_name
foo_process_name

With any POSIX sed:

$ sed 's/\([^_]\{1,\}_\)\{2\}\(.*\)\.txt$/\2/' file
processname
process_name
foo_process_name

With GNU awk:

$ awk '{$0=gensub(/([^_]+_){2}(.*)\.txt$/,"\\2",1)} 1' file
processname
process_name
foo_process_name

With any awk:

$ awk '{sub(/([^_]+_){2}/,""); sub(/\.txt$/,"")} 1' file
processname
process_name
foo_process_name

Could you please try following, written and tested with shown samples only.

awk 'match($0,/.*[0-9]+_/){print substr($0,RSTART+RLENGTH)}'  Input_file

Using :

$ awk 'BEGIN{FS="[[:digit:]]+_"} {gsub(/\..+$/,"", $2); print $2}' file
processname
process_name
foo_process_name

this works by setting a field separator FS to a digit followed by a _, and by stripping the resulting variable $2 of the file extension with gsub

It seems you need to tweak an existing awk script to accomodate this requirement. It's too bad awk doesn't come with a join function, but we can roll our own:

function join_fields(from, to, joiner,     result, i, sep) {
  for (i=from; i<=to; i++) {
    result = result sep $i
    sep = joiner
  }
  return result
}

Demo:

awk -F '[_.]' '
  function join_fields(from, to, joiner,     result, i, sep) {
    for (i=from; i<=to; i++) {
      result = result sep $i
      sep = joiner
    }
    return result
  }

  {
      field = join_fields(3, NF-1, "_")
      print $0, "\t", field
  }
' <<END
foobar_823932230_processname.txt
foobar_82393280_process_name.txt
foobar_8239330_foo_process_name.txt
END
foobar_823932230_processname.txt     processname
foobar_82393280_process_name.txt     process_name
foobar_8239330_foo_process_name.txt      foo_process_name
Related