bash sed/grep extract text between 2 words - regex

My problem is the same as it's here, except I only want the first occurrence, ignore all the rest:
How to use sed/grep to extract text between two words?
In his example if it would be:
input: "Here is a String Here is a String"
But I only care about the first "is"
echo "Here is a String Here is a String" | grep -Po '(?<=(Here )).*(?= String)'
output: "is a String Here is a"
Is this even possible with grep? I could use sed as well for the job.
Thanks

Your regexp happens to be matching against the longest string that sits between "Here" and "String". That is, indeed, "Here is a String Here is a String". This is the default behaviour of the * quantifier.
$ echo "Here is a String Here is a String" | grep -Po '(?<=(Here )).*(?= String)'
is a String Here is a
If you want to match the shortest, you may put a ? (greediness modifier) just after the * quantifier:
$ echo "Here is a String Here is a String" | grep -Po '(?<=(Here )).*?(?= String)'
is a
is a

To get the first word you can use grep -o '^[^ ]*':
echo "Here is a String Here is a String" | grep -Po '(?<=(Here )).*(?= String)' | grep -o '^[^ ]*'
And you can pipe grep to grep multiple times to compose simple commands into complex ones.

sed 's/ String.*//;s/.*Here //'

Related

Why is this grep <regex> not working?

The following regex with grep doesn't seem to be working:
grep "(?=\_\(\").*?(?=\"\))" ./testfile.js
testfile.js is as follows:
asoijf oaisdjf _("string 1") fodijsasf _("string 2")
fasdoij _("string 3");
console.log(_("string 4"));
My aim is to grab all the strings enclosed in _() function calls, without greps -P flag (the option doesn't exist for me). Expected output would be:
string 1
string 2
string 3
string 4
Any idea's?
Update: The -P flag was removed from bash in my version of mac (see grep -P no longer works how can I rewrite my searches)
Could you please try following awk and let me know if this helps you.
grep -oE '_\([^\)]*' Input_file | cut -c3-
Output will be as follows.
"string 1"
"string 2"
"string 3"
"string 4"
EDIT: Since OP doesn't have -P option in it's O.S so providing an awk approach here too.
awk '{
while($0){
match($0,/_\([^\)]*/);
st=RSTART;
le=RLENGTH;
if(substr($0,RSTART+2,RLENGTH-2)){
print substr($0,RSTART+2,RLENGTH-2)
};
$0=substr($0,st+va+3)
}
}
' Input_file
grep -oP '_\("\K[^"]+' inputfile
string 1
string 2
string 3
string 4
Here, -o will print only the matched result, not the whole line.
\K will be used for the look behind, Means matches the string left of \K but do not print it. [^"]+ means anything except " one or more time.
Or without -P option:
grep -oE '_\("[^"]+' inputfile|cut -d'"' -f2
string 1
string 2
string 3
string 4

How do I grab the part of this string after a `/`?

Here is my Bash code:
echo "Some string/Another string" | grep -o "\/.*"
This returns /Another string.
But I do not want the / included in the value returned by echo.
How do I change the regex do accomplish this?
EDIT: I want to match everything after the /, no matter what is after it. "Another string" is not always after the /.
If you have GNU Grep that supports PCRE then you can use \K to forget the match.
$ echo "Some string/Another string" | grep -oP "\/\K.*"
Another string
With sed :
$ sed 's/.*\/\(.*\)/\1/' <<< "Some string/Another string"
Another string
It search any characther up to next /, then capture and print following characters.
It may be more readable in ERE mode (-r option with GNU sed) and with another separator :
sed -r 's|.*/(.*)|\1|'
With parameter expansion:
$ string='Some string/Another string'
$ echo "${string#*/}"
Another string
The expansion with # removes what comes after it from the beginning of the expanded parameter.
With awk:
$ awk -F/ '{print $2}' <<< "$string"
Another string
This sets the field separator to / and prints the second field.
You can do this with cut command:
If you want string between first and second occurrence of /
cut -d '/' -f 2 <<< "Some string/Another string/abc"
output: Another string
If you want entire string after first occurrence of /
cut -d '/' -f 2- <<< "Some string/Another string/abc"
output: Another string/abc

How to match and keep the first number in a line using sed?

Question
Let's say I have one line of text with a number placed somewhere (it could be at the beginning, in the middle or at the end of the line).
How to match and keep the first number found in a line using sed?
Minimal example
Here is my attempt (following this page of a tutorial on regular expressions) and the output for different positions of the number:
$echo "SomeText 123SomeText" | sed 's:.*\([0-9][0-9]*\).*:\1:'
3
$echo "123SomeText" | sed 's:.*\([0-9][0-9]*\).*:\1:'
3
$echo "SomeText 123" | sed 's:.*\([0-9][0-9]*\).*:\1:'
3
As you can only the last digit is kept in the process whereas the desired output should be 123...
Using sed:
echo "SomeText 123SomeText 456" | sed -r 's/^[^0-9]*([0-9]+).*$/\1/'
123
You can also do this in gnu awk:
echo "SomeText 123SomeText 456" | awk '{print gensub(/^[^0-9]*([0-9]+).*$/, "\\1", $0)}'
123
To complement the sed solutions, here's an awk alternative (assuming that the goal is to extract the 1st number on each line, if any (i.e., ignore lines without any numbers)):
awk -F'[^0-9]*' '/[0-9]/ { print ($1 != "" ? $1 : $2) }'
-F'[^0-9]*' defines any sequence of non-digit chars. (including the empty string) as the field separator; awk automatically breaks each input line into fields based on that separator, with $1 representing the first field, $2 the second, and so on.
/[0-9]/ is a pattern (condition) that ensures that output is only produced for lines that contain at least one digit, via its associated action (the {...} block) - in other words: lines containing NO number at all are ignored.
{ print ($1!="" ? $1 : $2) } prints the 1st field, if nonempty, otherwise the 2nd one; rationale: if the line starts with a number, the 1st field will contain the 1st number on the line (because the line starts with a field rather than a separator; otherwise, it is the 2nd field that contains the 1st number (because the line starts with a separator).
You can also use grep, which is ideally suited to this task. sed is a Stream EDitor, which is only going to indirectly give you what you want. With grep, you only have to specify the part of the line you want.
$ cat file.txt
SomeText 123SomeText
123SomeText
SomeText 123
$ grep -o '[0-9]\+' file.txt
123
123
123
grep -o prints only the matching parts of a line, each on a separate line. The pattern is simple: one or more digits.
If your version of grep is compatible with the -P switch, you can use Perl-style regular expressions and make the command even shorter:
$ grep -Po '\d+' file.txt
123
123
123
Again, this matches one or more digits.
Using grep is a lot simpler and has the advantage that if the line doesn't match, nothing is printed:
$ echo "no number" | grep -Po '\d+' # no output
$ echo "yes 123number" | grep -Po '\d+'
123
edit
As pointed out in the comments, one possible problem is that this won't only print the first matching number on the line. If the line contains more than one number, they will all be printed. As far as I'm aware, this can't be done using grep -o.
In that case, I'd go with perl:
perl -lne 'print $1 if /.*?(\d+).*/'
This uses lazy matching (the question mark) so only non-digit characters are consumed by the .* at the start of the pattern. The $1 is a back reference, like \1 in sed. If there are more than one number on the line, this only prints the first. If there aren't any at all, it doesn't print anything:
$ echo "no number" | perl -ne 'print "$1\n" if /.*?(\d+).*/'
$ echo "yes123number456" | perl -lne 'print $1 if /.*?(\d+).*/'
123
If for some reason you still really want to use sed, you can do this:
sed -n 's/^[^0-9]*\([0-9]\{1,\}\).*$/\1/p'
unlike the other answers, this is compatible with all version of sed and will only print lines that contain a match.
Try this sed command,
$echo "SomeText 123SomeText" | sed -r '/[^0-9]*([0-9][0-9]*)[^0-9]*/ s//\1 /g'
123
Another example,
$ echo "SomeText 123SomeText 456" | sed -r '/[^0-9]*([0-9][0-9]*)[^0-9]*/ s//\1 /g'
123 456
It prints all the numbers in a file and the captured numbers are separated by spaces while printing.

regex: return characters inside parenthensis

I can't seem to get the values inside a parenthesis using grep.
echo "(this is a string)" | grep -Eo '[a-z ]*'
Ideally that should return the value inside the parenthesis, "this is a astring", instead it is not returning anything. Does anyone know the explanation?
This grep with -P (perl regex) works:
echo "foo (this is a string) bar" | grep -Po '\(\K[^)]*'
this is a string
OR using awk:
echo "foo (this is a string) bar" | awk -F '[()]+' '{print $2}'
this is a string
OR using sed:
echo "foo (this is a string) bar" | sed 's/^.*(\(.*\)*).*$/\1/'
this is a string
If you're trying to match everything enclosed by the parentheses, not including the parentheses, you should use this grep:
grep -Po '(?<=\()[^\)]*?'
The (?<=\() is a negative look-behind assertion that tells the regex engine to start from a character preceded by an opening parenthesis. [^\)]*? tells it to match all characters until it encounters a closing parenthesis. The -P tells it to use Perl regex syntax.

grep for X or Y in unix?

how can I capture all lines from a text file that begin with the character "X" or contain the word "foo"?
This works:
cat text | grep '^#' # begins with #
but I tried:
cat text | grep '^#|[foo]'
and variations but cannot find the right syntax anywhere. how can this be done?
thanks.
If your grep implementation isn't POSIX compliant, you can use egrep instead of grep:
egrep '^#|foo' text
cat text | grep '^#|foo'
does this. [foo] matches one character that's either an f or an o.
If you don't want to match parts of words like the foo in foobar, use word boundary anchors:
cat text | grep '^#|\bfoo\b'
contains the word "foo" is: (.*foo.*) so your regex would become:
cat yourFilePath | grep -E '^#|(.*foo.*)'