Reputation: 6011
In the current directory, I'd like to print the filename and contents in it. I can print filenames or contents separately by
find . | grep "file_for_print" | xargs echo
find . | grep "file_for_print" | xargs cat
but what I want is printing them together like this:
file1
line1 inside file1
line2 inside file1
file2
line1 inside file2
line2 inside file2
I read xargs with multiple commands as argument and tried
find . | grep "file_for_print" | xargs -I % sh -c 'echo; cat;'
but doesn't work. I'm not familiar with xargs, so don't know what exactly "-I % sh -c" means. could anyone help me? thank you!
Upvotes: 16
Views: 28378
Reputation: 24641
find . | grep "file_for_print" | xargs -I % sh -c 'echo %; cat %;'
(OP was missing %
s)
Upvotes: 32
Reputation: 241701
To start with, there is virtually no difference between:
find . | grep "file_for_print" | xargs echo
and
find . -name "file_for_print*"
except that the second one will not match filenames like this_is_not_the_file_for_print
, and it will print the filenames one per line. It will also be a lot faster, because it doesn't need to generate and print the entire recursive directory structure just in order for grep to toss most of it away.
find . -name "file_for_print*"
is actually exactly the same as
find . -name "file_for_print*" -print
where the -print
action prints each matched filename followed by a newline. If you don't provide find
with any actions, it assumes you wanted -print
. But it has more tricks up its sleeve than that. For example:
find . -name "file_for_print*" -exec cat {} \;
The -exec
action causes find to execute the following command, up to the \;
, replacing {}
with each matching file name.
find
does not limit itself to a single action. You can tell it to do however many you want. So:
find . -name "file_for_print*" -print -exec cat {} \;
will probably do pretty well what you want.
For lots more information on this very useful utility, type:
man find
or
info find
and read all about It.
Upvotes: 14
Reputation: 279
In this specific case, each command is executed for each individual file anyway, so there's no advantage in using xargs. You may just append -exec twice to your 'find':
find . -name "*file_for_print*" -exec echo {} \; -exec cat {} \;
In this case-print
could be used instead of the first echo
as pointed out by rici, but this example shows the ability to execute two arbitrary commands with a single find
Upvotes: 2
Reputation: 5407
Since it's not been said yet: -I %
tells xargs to replace '%' with the arguments in the command you give it. The sh -c '...'
just means run the commands '...'
in a new shell.
So
xargs -I % sh -c 'echo %; cat %;'
will run echo [filename]
followed by cat [filename]
for every filename given to xargs
. The echo and cat commands will be executed inside a different shell process but this usually doesn't matter. Your version didn't work because it was missing the %
signs inside the command passed to xargs
.
For what it's worth I would use this command to achieve the same thing:
find -name "*file_for_print*" | parallel 'echo {}; cat {};'
because it's simpler (parallel
automatically uses {}
as the substitution character and can take multiple commands by default).
Upvotes: 14
Reputation: 10653
What about writing your own bash function?
#!/bin/bash
myFunction() {
while read -r file; do
echo "$file"
cat "$file"
done
}
find . -name "file_for_print*" | myFunction
Upvotes: 1