ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Some members of the grep family can do this, for example, the non-standard cgrep:
Code:
#!/usr/bin/env bash
# @(#) s1 Demonstrate copying through the Nth occurrence of pattern.
# http://www.bell-labs.com/project/wwexptools/cgrep/
echo
set +o nounset
LC_ALL=C ; LANG=C ; export LC_ALL LANG
echo "Environment: LC_ALL = $LC_ALL, LANG = $LANG"
echo "(Versions displayed with local utility \"version\")"
version >/dev/null 2>&1 && version "=o" $(_eat $0 $1) cgrep
set -o nounset
echo
FILE=${1-data1}
echo " Data file $FILE:"
cat $FILE
echo
echo " Results:"
cgrep -N 2 -999999 "score" $FILE
# If only the matched lines are desired:
# cgrep -N 2 "score" $FILE
exit 0
Producing:
Code:
% ./s1
Environment: LC_ALL = C, LANG = C
(Versions displayed with local utility "version")
OS, ker|rel, machine: Linux, 2.6.26-2-amd64, x86_64
Distribution : Debian GNU/Linux 5.0
GNU bash 3.2.39
cgrep - (local: ~/executable/cgrep May 29 10:59 )
Data file data1:
one
two
three
four score
five
six score
seven
eight score
Results:
one
two
three
four score
five
six score
Of the many options for cgrep, these mean: consider only the first 2 matched lines, and copy (almost) a million lines that precede them (that is, in the case of a reasonable-length file, all the lines from the beginning of the file).
The URL in the script shows where to get cgrep. You will need a c compiler. I have compiled it in both 32 and 64-bit systems without trouble. It comes with a standard man page.
the others already have cleaner solutions to this but here's a bash script that will do the job. to use it type the script name, followed by the pattern, then the file name
Code:
#!/bin/bash
if [ $# != 2 ]; then
echo "USAGE: $0 pattern file"
exit 1
fi
#counter for pattern matches
count="0"
#pattern is first argument
pattern="$1"
#file is second argument
file="$2"
#for every line read, grep will try to match the pattern
#if pattern is found, counter increments by 1, once it reaches two it will exit the loop
while read line; do
echo $line
echo $line | grep -q $pattern
if [ $? == "0" ]; then
count=$[$count+1]
fi
if [ $count == "2" ]; then
break
fi
done <$2
If you stop and think about that (as you surf to and read an online copy of man awk ...), "you can do a helluva lot of goodness with a tool like that." And since it's based on the same technology that grep uses, you won't lose any speed (nor any sleep) over it. Nor will you have to, really, "write anything."
well, you can do the "grep" with bash without grep or other external tools
Code:
let count=0
while read -r line
do
case "$line" in
*PATTERN* ) count=$((count+1));;
esac
[ "$count" -eq 2 ] && echo $line && break
echo $line
done <"file"
that's also true, i didn't think about matching word per word.
however, since you're reading from a file, there are no backslashes correct? don't those only occur from STDOUT of commands? i assume that you're using the -r option to escape the backslashes that are used to denote spaces, in this case spaces between the words?
as a side question, not to hijack the thread, but i've come across this bit of code that i don't really understand.
Code:
read -d $'\000'
i know that -d denotes the delimiter, but what about $'\000'?
i mean, backslashes in the shell are used to denote spaces between words/filenames. in a file those backslashes aren't present between spaces. it's the difference between:
Code:
for line in $(ls); do
echo $line
done
and
Code:
file=$(ls)
for line in file; do
echo $file
done
isn't that why you're using the -r option, to make sure that read will read each word, instead of each line? or did i totally misunderstand something?
i mean, backslashes in the shell are used to denote spaces between words/filenames. in a file those backslashes aren't present between spaces. it's the difference between:
Code:
for line in $(ls); do
echo $line
done
first, don't iterate a list of files using ls and for like that. ls is redundant.
Code:
for file in *
do
echo $file
done
as for your read -r question. take a look at this example of input file
Code:
a b \ c
there's a backslash in the file. If there's no -r
Code:
$ while read line; do echo $line; done < "file"
a b c
But i want to keep the backslash, that's why i need it not to escape
Code:
$ while read -r line; do echo $line; done < "file"
a b \ c
first, don't iterate a list of files using ls and for like that. ls is redundant.
Code:
for file in *
do
echo $file
done
yeah i use the globbing feature, that was just to demonstrate what i was trying to get across.
ok so you're using -r to keep literal backslashes in the line, i understand that now.
then with this piece of code:
Code:
while read -r line
do
case "$line" in
*PATTERN* ) count=$((count+1));;
as i understand it, and i'm no expert so please bear with me, $line is the entire line correct? which would mean that *PATTERN* would also have to be the entire line to match, if it were only a word or part of a word, there would be no match.
or does the while-read-line construct read word per word? AFAIK it reads entire lines, but like i said i'm no expert
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.