tags:

views:

141

answers:

5

Updated : Thanks for your answers. As I said, my q was just a translation of my usecase. Let me get into more details of what I want to achieve. In my dev env, we use "ade" as our version control system what I want to do is :

ade describetrans | awk '/myapps/{ print $2 }' | sort -fr | xargs -iF ade unbranch F 

Now, every single time I run the unbranch command, a new file/dir gets checked out. so I need to run ade checkin -all after all my unbranch commands. So I needed something like

"pre part till sort" | xargs -iF (ade unbranch + ade checkin -all) 

Any way to run 2 commands on the op of pipe ?

Thanks

Original question asked :

I can translate the usecase I have into the following :

I need to get the 1st line of a file. I do

cat file | head -1 

Now I want to do this on a list of files. How do I do the following in one unix command ?? Eg :

find . -name "*.log" | ( xargs -iF cat F  | head -1 )

Obviously the brackets in the above command do not work.

is there a way to pipe the output of the find command and do 2 commands on it ( cat and head ) ? Tried using ; and && but dint help. I can create a script - but wanted to do this in one command.

Again - this is just a translation of the case I have.

thanks Rohan

+3  A: 

First of all, head accepts more than one file name, so you can simply write:

 head -q -n 1 *.log

or:

find . -name '*.log' -exec head -n 1 '{}' ';'

However, if you really need to duplicate the stream, then use tee and do something along the lines of:

 wget -O - http://example.com/dvd.iso | tee >(sha1sum > dvd.sha1) > dvd.iso

This example is taken from info coreutils 'tee invocation'.


UPDATE (Following the ade comment) In your case tee will not work. You need to perform a task after another task finishes, and tee will trigger the tasks more or less simultaneously (modulo buffering).

Another approach will work, provided that there are no spaces in the input lines (a serious issue, I know, but I'm not sure how to overcome it right now). I'll start with a generic solution:

echo -e 'Foo\nBar\nBaz' | ( for i in `cat` ; do echo 1$i ; echo 2$i ; done )

Here, echo -e 'Foo\nBar\nBaz' creates a sample multi-line input, echo 1$i is the first command to run, echo 2$i is the second command to run.

I'm not familiar with ade and I don't have it installed on my system, but I'm guessing that in your case something like this might work:

ade describetrans | awk '/myapps/{ print $2 }' | sort -fr | ( for i in `cat` ; do ade unbranch $i ; ade checkin -all $i ; done )
Bolo
This is fine, but you might replace the `;` with a (nonstandard) `+` to have `find` pass multiple names to one single `head` subprocess.
Philipp
@Philipp Oh, I didn't know about the `+`, it's very cool! Instead of altering my answer (again), I've upvoted your informative comment.
Bolo
Thanks for your answers. As I said, my q was just a translation of my usecase. Let me get into more details of what I want to achieve. In my dev env, we use "ade" as our version control systemwhat I want to do is : ade describetrans | awk '/myapps/{ print $2 }' | sort -fr | xargs -iF ade unbranch F Now, every single time I run the unbranch command, a new file/dir gets checked out. so I need to run ade checkin -all after all my unbranch commands. So I needed something like <pre part till sort> | xargs -iF (ade unbranch + ade checkin -all) Any way to run 2 commands on the op of pipe ?
sms169
@sms169 Please check the updated answer.
Bolo
A: 

You're making this much more complicated than it needs to be, by piping input into head rather than simply giving it a filename(s):

head -n1 `find -X . -name '*.log' | xargs`

If you don't need to traverse subdirectories, you can make it even simpler:

head -n1 *.log

You can screen out the filename headers by piping through grep: | grep -v '^(==> .* <==)?$' | grep -v '^$'

Ether
The first one won't work if a file name contains a space or newline.
Philipp
And the second one will additionally print headers with file names, unless you add `-q`.
Bolo
`-q` is nonstandard and doesn't exist on BSD systems.
Philipp
+1  A: 

Useless use of cat. Simply pass the file names to head:

find . -name '*.log' -exec head -n 1 '{}' '+'

or

find . -name '*.log' -print0 | xargs -0 head -n 1

EDIT: This will print headers for each file. On Linux, this can be suppressed with -q, but on other systems you must make sure that head is called with a single argument:

find . -name '*.log' -exec head -n 1 '{}' ';'
Philipp
A: 

Try

ade describetrans | awk '/myapps/{ print $2 }' | sort -fr | xargs sh -c 'ade unbranch "$@"; ade checkin -all "$@"' arg0

This is assuming that ade accepts multiple files at once and ade checkin -all needs to be called for every file.

The string arg0 supplies the value of $0 in the -c string.

jilles
A: 

I do not know ade, so I will have to guess what the 2 commands want to run really are. But if you have GNU Parallel http://www.gnu.org/software/parallel/ installed one of these should work:

ade describetrans | awk '/myapps/{ print $2 }' | sort -fr |
parallel -j1 ade unbranch {} ";" ade checkin -all {}

ade describetrans | awk '/myapps/{ print $2 }' | sort -fr |
parallel -j1 ade unbranch {} ";" ade checkin -all

ade describetrans | awk '/myapps/{ print $2 }' | sort -fr |
parallel -j1 ade unbranch {} ; ade checkin -all

If ade can be run in parallel, you can remove -j1.

Watch the intro video for GNU Parallel to learn more: http://www.youtube.com/watch?v=OpaiGYxkSuQ

Ole Tange