Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

how to pass file names get from grep to sed

12 views
Skip to first unread message

miloody

unread,
Nov 22, 2009, 2:13:43 AM11/22/09
to
Dear all:
suppose i want to replace cat with dog in all the files within a
folder, how could I reach that with grep and sed?
i try to use "grep -lr 'cat' * |sed -ei 's/cat/deg/' ", but sed seems
cannot eat the file names piped by grep.
How could I pass the file names to sed?
appreciate your help,
miloody

Janis Papanagnou

unread,
Nov 22, 2009, 2:41:06 AM11/22/09
to

Use xargs.

... | xargs sed ...


Janis

> appreciate your help,
> miloody

miloody

unread,
Nov 22, 2009, 2:58:58 AM11/22/09
to

Hi:
I write a shell script which first save the result of 'grep -lr 'cat'
*', and then use for loop feeding sed.
But why pipe command cannot work?
is there any rule about pipe, '|', command?
appreciate your help,
miloody

Chris F.A. Johnson

unread,
Nov 22, 2009, 3:01:49 AM11/22/09
to
On 2009-11-22, miloody wrote:

A pipe sends its output to the input of the next command; sed
expects filenames on the command line, not in its standard input.

--
Chris F.A. Johnson, author <http://shell.cfajohnson,com/>
===================================================================
Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress)
Pro Bash Programming: Scripting the GNU/Linux Shell (2009, Apress)
===== My code in this post, if any, assumes the POSIX locale =====
===== and is released under the GNU General Public Licence =====

Ben Finney

unread,
Nov 22, 2009, 3:02:16 AM11/22/09
to
miloody <mil...@gmail.com> writes:

> suppose i want to replace cat with dog in all the files within a
> folder, how could I reach that with grep and sed?

You'll need to get the file names to ‘sed’ on its own command line.

> i try to use "grep -lr 'cat' * |sed -ei 's/cat/deg/' ", but sed seems
> cannot eat the file names piped by grep.

Right, because you're piping the output of a process ‘grep …’ to the
input of another process ‘sed …’. But the ‘sed’ process is not looking
for file names on its standard input; it's expecting the file names on
its command line, and there are none there.

> How could I pass the file names to sed?

You can use ‘xargs(1)’ to read *its* standard input, and construct
command lines from the arguments that it reads.

Compare the output of this::

$ echo sed -ei 's/cat/dog/' foo bar baz

against the output of this::

$ grep -lr 'cat' * | xargs echo sed -ei 's/cat/dog/'

to understand what the role of ‘xargs’ is.

--
\ “The cost of education is trivial compared to the cost of |
`\ ignorance.” —Thomas Jefferson |
_o__) |
Ben Finney

miloody

unread,
Nov 22, 2009, 3:13:24 AM11/22/09
to
Hi

> > Hi:
> > I write a shell script which first save the result of 'grep -lr 'cat'
> > *', and then use for loop feeding sed.
> > But why pipe command cannot work?
> > is there any rule about pipe, '|', command?
>
>    A pipe sends its output to the input of the next command; sed
>    expects filenames on the command line, not in its standard input.
>
Can I get the conclusion that pipe is the command that always send the
result to the standard input of the next command?
if so, how could I tell which command gets its input from standard
input or command line?

take grep for example:
grep -rw 'cat' * -----> grep get its input from cmd line
find ./ -name '*.c' --> find get its input from cmd line

are these surmises correct?
thanks for your reply,
miloody

Ben Finney

unread,
Nov 22, 2009, 3:36:36 AM11/22/09
to
miloody <mil...@gmail.com> writes:

> Can I get the conclusion that pipe is the command that always send the
> result to the standard input of the next command?

Pipe is an operation within a command; but otherwise, yes, that's right.
Read the Bash man page (if you're using Bash) for more; piping is one of
the “redirection” operators.

> if so, how could I tell which command gets its input from standard
> input or command line?

By reading the documentation for each command that you want to use.
(Notice that standard input and command line are often *both* used, for
different purposes, among other sources of input.)

--
\ “Perhaps our role on this planet is not to worship God — but to |
`\ create Him.” —Arthur C. Clarke, 1972 |
_o__) |
Ben Finney

Chris F.A. Johnson

unread,
Nov 22, 2009, 3:38:24 AM11/22/09
to
On 2009-11-22, miloody wrote:
> Hi
>> > Hi:
>> > I write a shell script which first save the result of 'grep -lr 'cat'
>> > *', and then use for loop feeding sed.
>> > But why pipe command cannot work?
>> > is there any rule about pipe, '|', command?
>>
>> ? ?A pipe sends its output to the input of the next command; sed
>> ? ?expects filenames on the command line, not in its standard input.

>>
> Can I get the conclusion that pipe is the command that always send the
> result to the standard input of the next command?

A pipe connects the standard output of one command to the standard
input of another command.

> if so, how could I tell which command gets its input from standard
> input or command line?

A command gets its parameters from the command line; its input
usually comes from standard input or files specified as paramteres
on the command line.

> take grep for example:
> grep -rw 'cat' * -----> grep get its input from cmd line

It gets its input from the files specified by, in this case, *. If
no file is specified, it uses stdin as input.

> find ./ -name '*.c' --> find get its input from cmd line

Its input doesn't come from the command line; the command line
tells it where to get its input.

> are these surmises correct?

No.

bb

unread,
Nov 30, 2009, 11:00:09 AM11/30/09
to

Try to run the part you have before the pipe first and see what output you get.
Then you can figure out if sed will find any cats in that list.

When I do things like that I use a perl oneliner like
perl -pi -e 's,cat,dog,g' *

but since you will do it recursive you can take your file list you created with
your grep.

perl -pi -e 's,cat,dog,g' $(grep -lr 'cat' *) unless it's to long list, or
includes file names with spaces.

/bb


Rakesh Sharma

unread,
Dec 1, 2009, 6:53:52 AM12/1/09
to


You could do this many ways:

find . \
\! -name . -prune
-type f \
-exec \
grep -li 'cat' {} \;
-exec \
sed -ie 's/cat/dog/g' {} \;

for _f in ./* ./.[!.]* ./..?*; do
if [ -f "$_f" ] && [ ! -L "$_f" ]; then
if grep -li 'cat' < $_f > /dev/null; then
sed -ie 's/cat/dog/g' "$_f"
fi
fi
done

perl -e '
for (<./* ./.[!.]* ./..?*>) {
system("perl -i.BAK -0777e '/cat/ && s//dog/g' \"$_\"") if ! -l
&& -f _;
}
'

--Rakesh

John W. Krahn

unread,
Dec 2, 2009, 5:40:08 AM12/2/09
to

You don't need both a match operator and a substitution operator.

> && -f _;
> }
> '

And you don't have to start a separate process to run the substitution:

perl -e'
$^I = ".BAK";
local $/;
for ( grep !-l && -f _, <./* ./.[!.]* ./..?*> ) {
@ARGV = $_;
s/cat/dog/g while <>;
}
'

Or also as:

perl -i.BAK -0777pe's/cat/dog/g' ./* ./.[!.]* ./..?*

John
--
The programmer is fighting against the two most
destructive forces in the universe: entropy and
human stupidity. -- Damian Conway

Rakesh Sharma

unread,
Dec 3, 2009, 3:33:59 AM12/3/09
to
> human stupidity.               -- Damian Conway- Hide quoted text -
>
> - Show quoted text -

Thanks John for your masterly explanations.!

Has it ever crossed your mind (or O'Reilly's) to gather your perl-
one-
liner solutions from all over comp.unix.shell, comp.perl, etc.
& put them in a book form?

--Rakesh

0 new messages