Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have file with below contents .

/ABC/RTE/AD_900_VOP_123/OPP
/ABC/RTE/TRE/AD_900_VOP_145/BBB
/ABC/RTE/AN_900_VFP_124/FBF
/ABC/RTE/HD_900_FOP_153/WEW
/ABD/RDV/AD_900_VOP_123/OPP
/ABC/RTE/WD_900_VOP_123/GRR/TRD
/ABC/RTE/RTD/AR_900_VOP_443/SDD

How can I use regular expression on this file such that I get the output such as

AD_900_VOP_123
AD_900_VOP_145
AN_900_VFP_124
HD_900_FOP_153
AD_900_VOP_123
WD_900_VOP_123
AR_900_VOP_443
share|improve this question
1  
What is the criterion for picking the field of interest? –  1_CR Jun 25 '13 at 16:20
    
criteria is any pattern like <alphabets>_<digits>_<alphabets>_<digits> and fall between two / –  g4ur4v Jun 25 '13 at 16:26
1  
awk -F/ '{print $3}' –  Johan Jun 26 '13 at 11:18
    
awk -F/ '{print $(NF-1)}' to find last dir (if those are dirs) –  JJoao Jan 13 at 12:01

6 Answers 6

up vote 4 down vote accepted

Gnu grep

grep -oE '[[:alpha:]]+_[[:digit:]]+_[[:alpha:]]+_[[:digit:]]+' 

Use the perl-regex flag and look-behind and look-ahead assertions to guarantee that the match is surrounded by /

grep -oP '(?<=/)[[:alpha:]]+_[[:digit:]]+_[[:alpha:]]+_[[:digit:]]+(?=/)'
share|improve this answer

One way with awk:

awk -F/ '{for(i=1;i<=NF;i++)$0=($i~/_/)?$i:$0}1' file
share|improve this answer

IMHO Perl offers the easiest and the most flexible solution:

perl -nE 'say $1 if m{/(\w+\d+\w+\d+)/};' input_file

Please note that input_file is optional: STDIN will be filtered if/when input file name is not given.

share|improve this answer

This should do what you need.

Contents of tstfile.txt:

/ABC/RTE/AD_900_VOP_123/OPP
/ABC/RTE/TRE/AD_900_VOP_145/BBB
/ABC/RTE/AN_900_VFP_124/FBF
/ABC/RTE/HD_900_FOP_153/WEW
/ABD/RDV/AD_900_VOP_123/OPP
/ABC/RTE/WD_900_VOP_123/GRR/TRD
/ABC/RTE/RTD/AR_900_VOP_443/SDD

Command to transform tstfile.txt:

$ sed 's|.*/\([0-9_A-Z]\+900[0-9_A-Z]\+\)/.*|\1|' tstfile.txt
AD_900_VOP_123
AD_900_VOP_145
AN_900_VFP_124
HD_900_FOP_153
AD_900_VOP_123
WD_900_VOP_123
AR_900_VOP_443

explanation

The above extracts everything that's touching the "900" up to the first forward slash encountered at the beginning of "900" (left of the 9), and everything up to the first forward slash at the end of "900" (right of the last 0).

share|improve this answer
    
can you please explain it in one or two lines –  g4ur4v Jun 25 '13 at 16:29
    
Hi,I just ran it ,but I get the entire input as the result $ sed 's|.*/\([0-9_A-Z]\+900[0-9_A-Z]\+\)/.*|\1|' tstfile.txt /ABC/RTE/AD_900_VOP_123/OPP /ABC/RTE/TRE/AD_900_VOP_145/BBB /ABC/RTE/AN_900_VFP_124/FBF /ABC/RTE/HD_900_FOP_153/WEW /ABD/RDV/AD_900_VOP_123/OPP /ABC/RTE/WD_900_VOP_123/GRR/TRD /ABC/RTE/RTD/AR_900_VOP_443/SDD –  g4ur4v Jun 25 '13 at 16:41
    
No ,I am not :) –  g4ur4v Jun 25 '13 at 16:46
    
did you run it ? –  g4ur4v Jun 25 '13 at 16:47
    
@g4ur4v - Sorry I had to ask 8-). What version of sed are you using? I just ran what you sent me and it worked just fine. You can use this command: sed --version GNU sed version 4.2.1. –  slm Jun 25 '13 at 16:47
sed 's|.*/\([^/]*_[^/]*\)/.*|\1|
' <<\INPUT
/ABC/RTE/AD_900_VOP_123/OPP 
/ABC/RTE/TRE/AD_900_VOP_145/BBB 
/ABC/RTE/AN_900_VFP_124/FBF 
/ABC/RTE/HD_900_FOP_153/WEW 
/ABD/RDV/AD_900_VOP_123/OPP 
/ABC/RTE/WD_900_VOP_123/GRR/TRD 
/ABC/RTE/RTD/AR_900_VOP_443/SDD
INPUT

That will remove up to the second to last occurrence of / immediately preceding a _ character on a line, save everything between there and the next occurrence of /, and then remove the rest.

The above command prints....

AD_900_VOP_123 
AD_900_VOP_145 
AN_900_VFP_124 
HD_900_FOP_153 
AD_900_VOP_123 
WD_900_VOP_123 
AR_900_VOP_443
share|improve this answer
    
A variation on this which is slightly longer but 100 times easier to read (and write!) is sed 's|.*/\(.._..._..._...\)/.*|\1|' <input –  Johan Jan 13 at 11:48
    
@Johan - it is also far less capable - your version strictly delimits each field, mine will work with fields of any length. And I don't consider it easier to read or write. –  mikeserv Jan 13 at 15:34

The parts you do not want have a slash and three characters.

The part that you want to keep also starts with a slash and have more than three characters, but the third character is an underscore, so we delete all the parts that look like /XXX but not /XX_

This leaves the leading slash on the part that we want to keep, so we finally also delete that one slash.

sed 's|/..[^_]||g; s|^/||' </tmp/f1

Explanation:

The sed command is made up of two s (substitute commands) separated by ;. Since we have slash in the regular expression, I use s|...|...| in stead of the regular s/.../.../

Both of the substitute commands have the second part empty - substitute with nothing = delete that part. The first one uses a g for global, in other words do it over and over until there is nothing to substitute.

The [^_] matches anything except an underscore. T

share|improve this answer
    
Using . like that in a global is usually looking for trouble. What if one of the fields winds up being only a single char? That field (and one or two that follow) goes poof. sed 's|/[^/_]\{3\}||g' would at least serve to ensure that you don't remove anything you shouldn't, though in some cases might result in your not removing something you should, which is usually the better alternative, as I consider it. –  mikeserv Jan 13 at 17:10
    
@mikeserv It handles the sample data provided, not all possible types of data. –  Johan Jan 14 at 9:20

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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