4

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
4
  • 1
    What is the criterion for picking the field of interest?
    – iruvar
    Jun 25, 2013 at 16:20
  • criteria is any pattern like <alphabets>_<digits>_<alphabets>_<digits> and fall between two /
    – g4ur4v
    Jun 25, 2013 at 16:26
  • 1
    awk -F/ '{print $3}'
    – Johan
    Jun 26, 2013 at 11:18
  • awk -F/ '{print $(NF-1)}' to find last dir (if those are dirs)
    – JJoao
    Jan 13, 2015 at 12:01

6 Answers 6

7

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:]]+(?=/)'
3

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.

2

One way with awk:

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

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).

7
  • can you please explain it in one or two lines
    – g4ur4v
    Jun 25, 2013 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, 2013 at 16:41
  • No ,I am not :)
    – g4ur4v
    Jun 25, 2013 at 16:46
  • did you run it ?
    – g4ur4v
    Jun 25, 2013 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, 2013 at 16:47
0
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
2
  • A variation on this which is slightly longer but 100 times easier to read (and write!) is sed 's|.*/\(.._..._..._...\)/.*|\1|' <input
    – Johan
    Jan 13, 2015 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, 2015 at 15:34
0

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

2
  • 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, 2015 at 17:10
  • @mikeserv It handles the sample data provided, not all possible types of data.
    – Johan
    Jan 14, 2015 at 9:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .