Question

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!

Was it helpful?

Solution 2

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.

OTHER TIPS

find . | grep "file_for_print" | xargs -I % sh -c 'echo %; cat %;' (OP was missing %s)

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).

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

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top