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

I've been at this for two days now and I can't get it to work. Scripting and perl are brand new to me, so I'm sure there are at least a few nuances I'm missing. Anyways, I need the script to do three things:

1. make a list of all (and only) directories within the current directory
2. cd into each directory in the list
3. run foo.exe in each directory and return to the main directory

The main directory includes files in addition to the target directories.

#!/usr/bin/perl -w @file=`find * -type d`; foreach (@file) { `cd $_;/somepath/foo`; }

It's been suggested that I use the following in place of find * -type d

$foo=`ls -d *`; @file = split'',$foo;

Let me know if I'm missing any crucial info here, this is my first post in the monastery!

Replies are listed 'Best First'.
Re: trouble with a simple script
by toolic (Bishop) on Jul 07, 2010 at 18:52 UTC
    Update: See ikegami's reply for a working solution :)

    Here is one way to get just the subdirectories of the current directory (using File::Slurp, grep and -d), then run your command in each dir (using chdir and system):

    use strict; use warnings; use File::Slurp qw(read_dir); my @dirs = grep { -d } read_dir('./'); for (@dirs) { chdir $_; system '/somepath/foo.exe'; }

    If you want to run it in all directories recursively, then find or File::Find would be appropriate.

    You should be specific about the problem you are having. Why doesn't it work for you?

    You should choose a more meaningful title for your question, like: "Problem running command in subdirectories.".

      You need to undo the chdir since you are using paths relative to the original directory.

      Here's a convenient way of doing that:

      use File::chdir qw( $CWD ); use File::Slurp qw( read_dir ); my @dirs = grep -d, read_dir('.'); for (@dirs) { local $CWD = $_; system '/somepath/foo.exe'; }

      Or since we know we're just going one level down,

      use File::Slurp qw( read_dir ); my @dirs = grep -d, read_dir('.'); for (@dirs) { chdir $_ or die; system '/somepath/foo.exe'; chdir '..' or die; }

      Another way is to make the paths absolute.

      Update: Added alternative.

Re: trouble with a simple script
by zek152 (Pilgrim) on Jul 07, 2010 at 18:57 UTC

    For one thing you never change back to the initial (or main) directory. Do you have to change to that directory or can you just run "foo.exe" from the starting directory ie:

    `./$_/foo.exe`

    What platform are you writing this for? The commands you are running suggest a linux/unix platform but running a *.exe suggests Windows.

    Lastly, (and I understand that this is perlmonks.org so props for using Perl) you might want to look into using a shell scripting language (such as bash).

      For one thing you never change back to the initial (or main) directory.

      The script's directory never changes, only the child's, so that's not true. The only major problem in the OP's code is the extra "`" that prevents the script from even compiling. Also, if he's using cmd (i.e. not a cygwin build), ";" must be changed to "&".

      Lesser problems include lack of error checking, lack of proper conversion from strings to shell literals, and possibly useless use of backticks.

      Update: Added lesser problems.
      Update: He's not on Windows as I believed.

Re: trouble with a simple script
by apollo (Initiate) on Jul 07, 2010 at 19:26 UTC

    Thank you for the pointers!

    The script is run on a linux system, I used foo.exe to illustrate the fact that it is an executable- what's a less confusing way to designate an executable for unix/linux? The foo executable must be run from within the target directory, I don't believe it can be run on a directory.

    For the sake of improving my perl abilities I figured this would be a good script to write in perl, however the actual shell is tcsh.

    I've included the error message below; each time I run the script I get hundreds of these errors all with different and seemingly random characters after "cd:"- if "cd: 0:" means that it is attempting to cd into directory "0", then something must be wrong with the list, because the first scalar on that list should be 00112.

    sh: line 0: cd: 0: No such file or directory

      however the actual shell is tcsh

      No, system and backticks execute sh (cmd on Windows). You can always execute another shell explicitly (e.g. system(tcsh => (-c => $tcsh_command));) but you didn't.

      if "cd: 0:" means that it is attempting to cd into directory "0", then something must be wrong with the list, because the first scalar on that list should be 00112.

      I'm guessing that error is from using split'',$foo;. It should be split ' ', $foo.

      $ perl -E'say join ":", split "", "abc def"' a:b:c: :d:e:f $ perl -E'say join ":", split " ", "abc def"' abc:def

      That said, if you're going to use ls,

      my $dirs = `ls -d *`; my @dirs = split ' ', $dirs;
      can be simplified to
      chomp( my @dirs = `ls -d *` );

      Update: hum, it's not really any simpler.

Re: problem running executable in subdirectories
by apollo (Initiate) on Jul 07, 2010 at 19:54 UTC

    Having removed the extra "`" I end up with the following errors:

    sh: -c: line 1: syntax error near unexpected token `;' sh: -c: line 1: `;/somepath/foo'

    "Lesser problems include lack of error checking, lack of proper conversion from strings to shell literals, and possibly useless use of backticks."

    By error checking do you mean that I should be using print, for example, after the script cd's into each directory as a check to see if the script is indeed cd'ing into each directory?

    Could you elaborate on the latter two? Where is the script converting from strings to shell literals and what should I do to remedy it? Are the backticks simply unnecessary or do they need to be replaced by system or exec? Like I said, I'm new to this and I'm learning by trial and error.

      Having removed the extra "`" I end up with the following errors:

      Please show the code.

      By error checking do you mean that I should be using print,

      print doesn't check for errors. I mean you should avoid running your program in the wrong directory, and the program's user probably wants to be notified of the failure to process a directory.

      Could you elaborate on the latter two?

      Your script will fail if and of the directories contains any special characters. For example, a directory with a space or a quote in its name.

      You're using backticks to capture the output of the program, then you discard the result. If you're trying to silence the child's STDOUT, that's ok. If not, you should be using system.