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 a file abc.txt the contents are

<classpathentry kind="src" path="Sources"/>
<classpathentry kind="con" path="WOFramework/ERExtensions"/>
<classpathentry kind="con" path="WOFramework/ERJars"/>
<classpathentry kind="con" path="WOFramework/ERPrototypes"/>
<classpathentry kind="con" path="WOFramework/JavaEOAccess"/>
<classpathentry kind="con" path="WOFramework/JavaEOControl"/>
<classpathentry kind="con" path="WOFramework/JavaFoundation"/>
<classpathentry kind="con" path="WOFramework/JavaJDBCAdaptor"/>

I want to copy all the paths into another file. That is I want my output text file to look like:

    WOFramework/ERExtensions
    WOFramework/ERJars
    WOFramework/ERPrototypes
    WOFramework/JavaEOAccess
    WOFramework/JavaEOControl
    WOFramework/JavaFoundation
    WOFramework/JavaJDBCAdaptor
share|improve this question
    
you want to copy depending on kind? –  harish.venkat May 27 at 18:06
1  
Looks like you're trying to extract parts of an XML document. Try an XML tool such as xmlstarlet or xmllint. stackoverflow.com/questions/91791/… –  Mikel May 27 at 19:44
5  
cut -d '"' -f4? –  Stéphane Chazelas May 27 at 20:02
1  
@StephaneChazelas, your answer should be the best solution :) –  Ramesh May 27 at 20:04

10 Answers 10

up vote 8 down vote accepted

I assume the file follows the same pattern. If that is the case, you can have a command like below.

grep -o ' path=.*$' file.txt | cut -c8- |rev | cut -c 4- | rev

So, I open the file using cat and then I extract only the characters from path= and then I remove the unwanted characters using cut and then I use the rev technique to remove unwanted characters from the end.

Another awk approach

awk -F'path="' '{print $2}' file.txt |rev | cut -c 4- | rev

I use the path=" as delimiter and print all the information after it. And the rev basically does the same as above.

Testing

cat file.txt
<classpathentry kind="src" path="Sources"/>
<classpathentry kind="con" path="WOFramework/ERExtensions"/>
<classpathentry kind="con" path="WOFramework/ERJars"/>
<classpathentry kind="con" path="WOFramework/ERPrototypes"/>
<classpathentry kind="con" path="WOFramework/JavaEOAccess"/>
<classpathentry kind="con" path="WOFramework/JavaEOControl"/>
<classpathentry kind="con" path="WOFramework/JavaFoundation"/>
<classpathentry kind="con" path="WOFramework/JavaJDBCAdaptor"/>

After running the command,

Sources
WOFramework/ERExtensions
WOFramework/ERJars
WOFramework/ERPrototypes
WOFramework/JavaEOAccess
WOFramework/JavaEOControl
WOFramework/JavaFoundation
WOFramework/JavaJDBCAdaptor

A better approach as provided by Stephane in comments.

cut -d '"' -f4 file.txt
share|improve this answer
    
He doesn't want "Sources" –  mikeserv May 27 at 19:37
    
Actually, I guess he accepted it - so what do I know? –  mikeserv May 27 at 19:58

A simple approach with awk:

awk -F\" '/WOF/ {print $4}' abc.txt > outfile
  • -F\" changes the field separator from the default (a space) to a quote mark (escaped with \)
  • /WOF/ restricts the returned results of each record (line of the file) to those that match the pattern: WOF
  • $4 is the fourth field for each of those matching records, the path.
share|improve this answer

Another approach with grep and cut:

grep "kind=\"con\"" sample.txt | cut -d \" -f 4 > sample_edited.txt

This will grep all lines containing kind="con" and print the paths by setting cut's delimiter to ".

share|improve this answer
sed -n '/.*="con"[^"]*./{s///;s/..>//p}' <<\DATA

<classpathentry kind="src" path="Sources"/>
<classpathentry kind="con" path="WOFramework/ERExtensions"/>
<classpathentry kind="con" path="WOFramework/ERJars"/>
<classpathentry kind="con" path="WOFramework/ERPrototypes"/>
<classpathentry kind="con" path="WOFramework/JavaEOAccess"/>
<classpathentry kind="con" path="WOFramework/JavaEOControl"/>
<classpathentry kind="con" path="WOFramework/JavaFoundation"/>
<classpathentry kind="con" path="WOFramework/JavaJDBCAdaptor"/>
DATA

OUTPUT

WOFramework/ERExtensions
WOFramework/ERJars
WOFramework/ERPrototypes
WOFramework/JavaEOAccess
WOFramework/JavaEOControl
WOFramework/JavaFoundation
WOFramework/JavaJDBCAdaptor

This should get only the WO... stuff, I think. It's also fully portable.

share|improve this answer

Another solution if your version of grep supports PCRE-style lookarounds

grep -oP '(?<=kind="con" path=").+?(?="/>)' abc.txt
share|improve this answer

With sed

sed -e 's/.*path="//' -e 's:"/>$::' abc.txt > output_file
share|improve this answer
    
sed -e 's/.*path="//' -e 's:".*$::' abc.txt > output_file -- dropping everything after the last quote instead of specific matching at the end. –  mdpc May 27 at 20:01
    
it display extra lines. –  Avinash Raj May 28 at 5:52
    
@AvinashRaj, where are you seeing extra lines in the OP's input data? The answer above is tailored to the OP's data. –  1_CR May 28 at 17:38
    
it displays sources also according to the op's input. –  Avinash Raj May 28 at 18:05

If the file format is really fixed, then the approach below is not as efficient as many of the other answers you already got.

So this is in the case that the file format changes or can not be relied upon (or turns out it can't be relied upon after doing a "brute force" extraction and coming up with "paths" such as kind=). Unfortunately, my experience is that "constant and guaranteed" formats simply aren't. Or not for long.

First you convert all the tags into newlines, so that you don't have to worry about multiple tags in one line or text disposition.

tr "<>" "\n\n" < source.txt

then you select the rows containing the single word "path" followed by space(s) and an equal sign

| grep "\<path\\s*="

from these lines, you extract the "path" component; this way you do not have to worry about what happens if some tags have attributes in a slightly different format

| sed -e 's/.*path\s*=\s*"\([^"]*\)".*/\1/'
# You can modify the above to handle single quotes as well as double quotes
# using [\'"] instead of "

and finally, possibly, you may want to get each path only once

| sort | uniq

Wrapping it in a single line,

tr "<>" "\n\n" < source.txt | grep "\<path\\s*=" | sed -e 's/.*path\s*=\s*"\([^"]*\)".*/\1/' | sort | uniq > output.txt
share|improve this answer

Since nobody has posted one yet, here are a few Perl solutions:

perl -ne  's/.*con.*="(.+)".*/$1/ && print' file

Explanation

The -ne means "Read the input file line by line and apply the script passed by -e". The s/foo/bar/ is the substitution operator, it will replace foo with bar. In this case, the replacement will be whatever was matched in the parentheses, this is $1. The regex means "match everything up to con, then the longest string until a = and then capture everything between the quotes. The && print will print the modified line only if the replacement was successful.

perl -e  'print grep{s/.*con.*=.(.+)".*/$1/}<>' file

Explanation

This one is a bit more idiomatic. It will print the result of applying the same substitution as used above to each line of the input file (<>). Just a different way of writing the same basic approach.

perl -F'[="]' -lane 'print $F[5] if $F[2]=~/con/' file

Explanation

The -a makes perl behave like awk, it automatically splits the input line into fields (saved as the @F array) on the character(s) passed by the -F parameter. Since I tell it to split on = or ", the 5th field will be what we're after and it is only printed if the 2nd field matches con. The -l adds a newline to each print call (and other things that are not relevant).


And here's another grep one. This will print all matches of letters/letters, it works correctly on your example but might not on a more complex one:

grep -Eio '[a-z]+/[a-z]+' file

And a pure shell one (bash/zsh/ksh):

while IFS='=' read a b c; do 
    [[ "$b" =~ "con" ]] && a=${c/%?\/>/} && echo ${a/#?}; 
done <  file 

Explanation

The while read; do ... ; done < file loops through each line of the file. Setting IFS to = splits each line at = and read a b c saves each field in the variables $a through $c. Then, if $b matches con, the last three characters are removed from $c and the result is saved as $a and then printed with the first character (the quote) removed. See here for more on bash's string manipulation options.

share|improve this answer

And the one through GNU sed's backreferencing,

sed -nr 's/^.*kind=\"con\" path=\"([^"]*)\".*$/\1/p' file

Example:

$ cat aa
<classpathentry kind="src" path="Sources"/>
<classpathentry kind="con" path="WOFramework/ERExtensions"/>
<classpathentry kind="con" path="WOFramework/ERJars"/>
<classpathentry kind="con" path="WOFramework/ERPrototypes"/>
<classpathentry kind="con" path="WOFramework/JavaEOAccess"/>
<classpathentry kind="con" path="WOFramework/JavaEOControl"/>
<classpathentry kind="con" path="WOFramework/JavaFoundation"/>
<classpathentry kind="con" path="WOFramework/JavaJDBCAdaptor"/>
data

$ sed -nr 's/^.*kind=\"con\" path=\"([^"]*)\".*$/\1/p' aa
WOFramework/ERExtensions
WOFramework/ERJars
WOFramework/ERPrototypes
WOFramework/JavaEOAccess
WOFramework/JavaEOControl
WOFramework/JavaFoundation
WOFramework/JavaJDBCAdaptor
share|improve this answer

You could do it like this:

while IFS=\" read -r _ _ _ f4 _; do
    case $f4 in
        */*) echo "$f4";;
    esac
done < file
share|improve this answer

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.