6

I have the file with text:

1861_ASSET-D_T_201702181000-201702181045_HN_
197895_STRING-H_T_201702181000-201702181045_HN_
14512861_FILE-FD_T_201702181000-201702181045_HN

How I can allocate text between first and second matches "_" using egrep, awk or sed?

I want to have:

ASSET-D
STRING-H
FILE-FD
  • 1
    Only those three languages? If so, why? – Jacob Vlijm Feb 20 '17 at 13:14
10
$ sed -r 's/[^_]+_([^_]+)_.*/\1/' file
ASSET-D
STRING-H
FILE-FD

Explanation

  • -r Use ERE
  • s/old/new/ replace old with new
  • [^_]+ some characters that are not underscore
  • (some chars) save some chars for later
  • .* any number of any characters
  • \1 the saved pattern
10

You can use awk with the following parameters:

  • -F "_" - which uses _ as separator
  • '{print $2}' - which prints the 2nd element

    $ awk -F  "_" '{print $2}' input_file
    ASSET-D
    STRING-H
    FILE-FD
    
9

If you want to allow other tools, cut would be the most simple solution:

cut -d _ -f 2 < input.txt > output.txt
6

A couple of perl approaches:

$ perl -F_ -lae 'print $F[1]' file 
ASSET-D
STRING-H
FILE-FD

The -a makes perl act like awk, splitting each input line on the character given by -F and saving each resulting field in the array @F. Therefore, printing $F[1] will print the 2nd field (arrays start counting at 0).

Alternatively:

$ perl -pe 's/.*?_(.+?)_.*/$1/' file 
ASSET-D
STRING-H
FILE-FD

The -p means "print every input line after running the script given by -e on it". The s/from/to/ is the substitution operator. It will replace from with to. Here, I am matching 0 or more characters (.*) but making it "non-greedy" (.*?); that ? makes it stop at the shortest possible match. Therefore, .*?_ will match everything up to the 1st _. The next part, (.+?)_ matches one or more characters until the next _ and then we match everything else until the end of the line (.*). The parentheses "capture" the match, and make it available in the replacement side of the operator as $1. Therefore, the whole thing will just replace the entire line with whatever was inside the parentheses.


You could also just do this with grep:

$ grep -oP '^.*?_\K[^_]+' file 
ASSET-D
STRING-H
FILE-FD

The -o means "print only the part of each line that matched" and the -P turns on Perl Compatible Regular Expressions (PCRE) which give us \K. The \K, in turn, means "ignore everything matched until this point". This means that we can use the ^.*?_ to matche everything until the first _ and then use \K to discard it so it isn't printed. Then, we just need as many non-_ characters as we can find ([^_]+).

6

As a python one- liner:

python3 -c "[print(l.split('_')[1]) for l in open('f')]"

where 'f' is your file, in quotes, e.g.:

python3 -c "[print(l.split('_')[1]) for l in open('/home/jacob/blub')]"

ASSET-D
STRING-H
FILE-FD

Explanation

for l in open('f')

reads f per line, and

l.split('_')[1]

splits the line by the delimiter _, keeps the second string (index [1], where [0] is the first.

print(l.split('_')[1]) 

subsequently prints the found string.

  • If you like functional style but don't want to abuse list comprehensions in Python: import sys, operator, functools; print(*map(operator.itemgetter(1), map(functools.partial(str.split, sep="_"), sys.stdin)), sep="\n") ;-] – David Foerster Mar 16 '18 at 18:37

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.