Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

using pipes in a system() call

by mcarlson (Initiate)
on Dec 07, 2005 at 18:31 UTC ( #514962=perlquestion: print w/replies, xml ) Need Help??

mcarlson has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to run a relativly simple command via system(), but it seems it is not piping the output. Basically I'm trying to do this:
system("find $home[$i]$dir -user $UID -print | xargs -n 1 chown -h $NE +WUID");
When it runs, chown prints out an error saying its missing an argument.

Any pointers?

Mike C

Replies are listed 'Best First'.
Re: using pipes in a system() call
by philcrow (Priest) on Dec 07, 2005 at 18:41 UTC
    When debugging problems like this, I usually form the string that will go to system, then print it. This helps me find bad interpolations, etc.

    On a side note, you can chown in Perl. Combining it with File::Find could make this tool pure Perl, reducing all sorts of possible problems.


Re: using pipes in a system() call
by Fletch (Bishop) on Dec 07, 2005 at 18:40 UTC

    A good debugging technique for problems like this is to build the command in a scalar and then pass that to system. This way you can examine what's actually getting passed to the shell (either in the debugger or by adding something like print "## \$cmd: ", $cmd, "\n" to your code). It may be the case that one or more of the variables you're trying to interpolate don't contain what you think they do (e.g. $home[$i] might be empty or doesn't end in a "/").

Re: using pipes in a system() call
by idsfa (Vicar) on Dec 07, 2005 at 18:45 UTC

    Your command has a syntax error. You are missing a semicolon. It is also possible that $UID is not defined when you try to use it.

    ... -print \; | ...

    The intelligent reader will judge for himself. Without examining the facts fully and fairly, there is no way of knowing whether vox populi is really vox dei, or merely vox asinorum. — Cyrus H. Gordon
Re: using pipes in a system() call
by bluto (Curate) on Dec 07, 2005 at 20:41 UTC
    Be very careful about using find in this manner. If you know that file paths are always well formed, this is ok. I highly suggest you look into using the -print0 option of find and the corresponding '-0' (minus zero) of xargs, which most modern versions support. Update: Well most *should* support it but one of the boxes I used didn't appear to so perhaps thats a GNU-ism?

    The main problem is when a user starts naming files with strange characters (esp spaces) in them these are not usually interpreted properly by xargs, even if you are only passing 1 argument via xargs' -n option. For example if a user has created a file named 'foo', a directory named 'foo ', a directory located under 'foo ' named 'etc', you will not be happy with the results of your chown (i.e. /etc will end up being chowned).

Re: using pipes in a system() call
by mcarlson (Initiate) on Dec 07, 2005 at 19:07 UTC
    So here is the entire program, this is just a prototype but its just looking through the many home directories we have to find files owned by $UID, and chown's them to the new UID($NEWUID):
    #!/usr/local/bin/perl my $UID = 1202; my $NEWUID = 51617; my @home = ( "/home1/", "/home2/", "/home3/", "/home4/", "/home5/", "/home6/", "/home7/", "/home8/", "/home9/", "/home10/", "/home11/", "/home12/", "/home13/", "/home14/" ); my $i = 0; foreach( @home ) { opendir DH, $home[$i] or die "Cannot open $home[$i]: $!"; foreach $dir ( readdir DH ) { next if $dir eq "." or $dir eq ".." or $dir eq ".snapshot" or +$dir eq "TT_DB"; print "find $home[$i]$dir -user $OUN -print | xargs -n 1 chown + -h $NEWUID\n"; system("find $home[$i]$dir -user $OUN -print | xargs -n 1 chow +n -h $NEWUID"); } $i++; }
    the output is so:
    find /home1/castillo -user 1202 -print | xargs -n 1 chown -h 51617 sh: syntax error at line 1: `|' unexpected
    So the command I'm giving it prints out exactly as expected, and running that by hand does work. I guess I don't understand why a | is 'unexpected'. I did try `-print \; |` but that doesn't work either.

    Thanks to all who replied.

    Mike C
      With a few small changes to avoid trashing my system, and after changing $OUN to $UID, this works for me.

      Maybe it's something weird with your system or your shell?. What kind of system are you on? What is $ENV{SHELL} set to? Does system("ls |cat"); work?

      Or maybe it's an invisible character (like a carriage return) in one of your strings. Try piping the program through cat -e to show any nonprinting characters.

      A third idea is to strace/truss/ktrace your program, to see what it's actually running.

      Hi Mike,

      Since you are looking for files in a specified set of directories, you may want to take a look at File::Find -- no sense in reinventing the wheel ;-)

      Jason L. Froebe

      Team Sybase member

      No one has seen what you have seen, and until that happens, we're all going to think that you're nuts. - Jack O'Neil, Stargate SG-1

Re: using pipes in a system() call
by swkronenfeld (Hermit) on Dec 07, 2005 at 18:42 UTC
    Why don't you add a line before your system call...

    print "find $home[$i]$dir -user $UID -print | xargs -n 1 chown -h $NEW +UID\n"; system("find $home[$i]$dir -user $UID -print | xargs -n 1 chown -h $NE +WUID");
    Now you can see what it's expanding into with the variable interpolation. I'm guessing the variables aren't expanding into what you think they are.

    Edit: Fletch's response to this node points out the dangerous situation that my suggestion can lead to. I'm leaving it in place as it's a valuable lesson to be learned.

      Great idea, then he'll have two copies of the same string which can very easily get out of sync with each other leading to even more fun to track down bugs when it starts printing one thing and executing another!

      (This is why several people have recommended building the command in a scalar and print the scalar before passing it to system; change it once change it everywhere).

        Valid point Fletch.

        I wrote it the way I did because I was thinking of a short-term debugging effort, where the OP would erase the line after seeing what it expanded to. When passing shorter length system calls (and this applies to other functions as well), I prefer to have the command written inside the call, rather than forcing my brain to stop reading along with the flow of the program and backtrack to the $command variable definition.

        Regardless, not knowing how the OP would be using our advice though, I should not have posted like this.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://514962]
Approved by jfroebe
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (3)
As of 2022-10-03 08:48 GMT
Find Nodes?
    Voting Booth?
    My preferred way to holiday/vacation is:

    Results (13 votes). Check out past polls.