3

Let us assume I have a file with lines like:

/java/jdkxx/jvm_jdk/bin/opt
/java/jre/jre_jvm/bin/opt
/foo/bar/bin/other/stuff/here

Is there a way I can extract part of the lines upto bin. I mean, assume those lines are at file.txt then

$ <some_command> file.txt 
/java/jdkxx/jvm_jdk/bin/
/java/jre/jre_jvm/bin/
/foo/bar/bin/
1
  • 2
    Does all lines in the file has only one /bin in it? What happens if you had a line like /java/jdkxx/jvm_jdk/bin/opt/bin? Mar 11, 2015 at 7:44

5 Answers 5

8

There are many ways to do this. Here are some:

# greedily caputure up to the last slash
grep -o '.*/bin/' file.txt
# remove all non-slash chars from the end of each line
sed 's#\(/bin/\).*$#\1#' file.txt
# using slash as a delimiter, blank out the last field
awk -F/ -v OFS=/ '{for (i=1; i<=NF; i++) if ($i == "bin") {NF=i; break}} 1' file.txt
2
  • I just wanted the total part but only upto bin/ ... I dont want whats there after bin/... and sometimes no guarantee that /bin/opt just, it may be /bin/opt/some/part. Please consider this case too
    – Raja G
    Mar 10, 2015 at 14:52
  • Gotcha, missed that requirement. Mar 10, 2015 at 14:56
4

A pure bash way:

while read -n line
do
    [[ $line =~ /bin/ ]] && printf "%s\n" "${line/%\/bin\/*//bin/}"
done
4

What, no Perl?

perl -ne 's#/bin\K.*## && print' file

If you know that all lines contain the pattern you want, you can simplify to:

perl -pe 's#/bin\K.*##' file

The \K is a PCRE expression that means "ignore everything before the \K".


You can also do things like

awk -F"/bin" '{print $1FS}' file

That sets awk's field delimiter (FS) to /bin, and then prints the first field and the value of FS (which is /bin). That one, again, assumes you want every line. If not, use this one instead:

awk -F"/bin" '($2){print $1FS}' file
1
  • Indeed Perl was missing. choosing # as a regex delimiter is also a good practice with paths patterns. +1 Mar 10, 2015 at 16:06
3

In python:

python3 -c "for l in open('f').readlines(): print(l[:l.find('/bin')+5])"

/java/jdkxx/jvm_jdk/bin/
/java/jre/jre_jvm/bin/

where f is the path to the file (in single quotes).

1
  • awesome but I would love that if its in bash.
    – Raja G
    Mar 10, 2015 at 14:53
2

Along with other good answers, you can also try the following which will make sure whatever there is after /bin/, will not be printed:

grep -Po ".*/(?<=/bin/)" file

Example:

$ cat test_file 
/java/jdkxx/jvm_jdk/bin/opt
/java/jre/jre_jvm/bin/opt/home

$ grep -Po ".*/(?<=/bin/)" test_file 
/java/jdkxx/jvm_jdk/bin/
/java/jre/jre_jvm/bin/

Here we are using the PCRE with positive lookbehind (?<=/bin/) to make sure that we take only upto the /, where we have /bin/ at last.

6
  • 1
    This looks very promising to me
    – Raja G
    Mar 10, 2015 at 15:03
  • That looks distinctly odd to me. Lookaheads are supposed to be zero width, they shouldn't appear in the matched output. Wait. Those are lookbehinds. Ah. Rather convoluted. Glen's way is much better.
    – muru
    Mar 10, 2015 at 15:19
  • @muru Let my try all the commands at home PC , office PC I cant do all this.
    – Raja G
    Mar 10, 2015 at 15:30
  • Note that this will also match on lines that do not contain /bin. For example This is a rubbish bin. Also, I don't see the point of the lookbehind. What does it offer that grep -Po '.*bin/' doesn't?
    – terdon
    Mar 10, 2015 at 16:10
  • @terdon: The lookbehind pattern is bin/, i think this won't match This is a rubbish bin..i think you probably meant This is a rubbish bin/..in that case i will modify the answer (Please correct me if i am wrong)....i have posted it as another way that can use lookarounds, might be helpful in some other similar cases..you are absolutely right, it won't give any extra benefit in this case than what you have mentioned..
    – heemayl
    Mar 10, 2015 at 16:55

You must log in to answer this question.

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