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

HI,

I haven't used Perl in my life before, and in order to automatize my analysis, I need to use a Perl script. In this script, there is a loop (I hope it is the correct term!) and the script should run the analysis for each subject's folder I defined. However, it only ran it for the first subject's folder and did not go to the second folder as it was defined. The script does not give any error that I could share here. So, I'll only share the script here and the message on the terminal when it ran the analysis for the 1st subject. Could you please suggest how to make this loop work? Looking forward to hearing your suggestion! Thanks in advance.

---- The script:
#!/usr/bin/perl # go to directory with subject-specific sub-directories cd .. #or die "directory not changed!\n"; opendir(DIR,"."); @files = ("SubjectASD202","SubjectASD203"); print "@files"; #say something stupid and unnecessary print "\n Thanks, I'll do the rest for you, just wait a second. \n"; $s = 0; foreach (@files) { # do second level statistical analysis run by run # go to the directory where the fsf-file for stats and post-stats +is stored chdir "/mnt/hgfs/ExpAutism/Scripts"; # create a copy of the fsf file #original file is needed as basis for all subjects: Level1Run1.fsf + - specific file is being overwritten for each run:Level1Run1_spec.fs +f use File::Copy; #create string that will be used for foe-wildcard replacement copy ("Level1Run1.fsf","Level1Run1_spec.fsf") or die "Copy fai +led"; use Tie::File; tie @array, 'Tie::File', "Level1Run1_spec.fsf" or die "arrayin +g failed"; # replace dummies for user and subject with specific values + for (@array) { #replace wildcards with specific titles of subjects s/Subjectx/$files[$s]/g; } untie @array; print "working on the stats concerning $files[$s]\n"; `/usr/local/fsl/bin/feat Level1Run1_spec.fsf`; $s=$s+1; } print "\n +++ DONE +++ \n";
----

The message while running the first subject's analysis:

SubjectASD202 SubjectASD203 Thanks, I'll do the rest for you, just wait a second. working on the stats concerning SubjectASD202

At the end, it only ran the analysis for the SubjectASD202 and did not run it for the SubjectASD203.

Replies are listed 'Best First'.
Re: Loop is not working
by haukex (Archbishop) on Oct 03, 2018 at 19:38 UTC

    Welcome to Perl and PerlMonks, ozdemmer!

    There are quite a few things that should be improved in your script. First of all, you should start your script with the following, which is explained in Use strict and warnings and the Basic debugging checklist:

    #!/usr/bin/perl use warnings; use strict; use diagnostics;

    When you do this, you will unfortunately see quite a few errors that you need to fix. For example, cd .. is not valid Perl (chdir is the right function), and you'll need to declare variables like @files and $s before using them. Also, you should be checking for errors in several more places, such as opendir (opendir my $dh, '.' or die "opendir: $!";) and chdir. This might seem a bit tedious at first, but in the long run, it will make for much better code, which is why these are important habits to get into.

    I think you might benefit from a read of perlintro. Among other things, you'll be introduced to some of the basic ways to process files. Tie::File is a good start, but note that it is quite inefficient - when you're processing files line-by-line, a while loop will be better. A few more notes:

    • You do opendir(DIR,".");, but then don't do anything with DIR. You only need opendir if you'll be getting a list of files in that directory, but since you have a fixed list of files, it doesn't look like you need this. If you do want to list files, you could take a look at glob, but please note that it has several caveats - but if you use it with a fixed string as an argument (like my @files = glob('*');), it should be fine.
    • You're using backticks (`...`) to run an external command and get its output, but then are not doing anything with that output. You might want to use system instead, and you should always check for errors. The bare minimum I suggest is die "exited with \$?=$?" if $?;, see $?, and I wrote at length about using modules to do this much better here. (Update: In this case, I might recommend IPC::System::Simple, which takes care of the error handling for you.)
    • You're doing foreach (@files), which will put each filename in the special variable $_. I would recommend that you use a named variable, such as foreach my $file (@files), this will make for much easier to read code.

    Now, on to your issue: You say you want to work on multiple files, but I see that you are doing chdir "/mnt/hgfs/ExpAutism/Scripts"; (a fixed directory) and then operating on a file Level1Run1_spec.fsf (also a fixed filename). I think your loop probably is running twice, but you're always operating on the same files. I don't see where the filenames you've specified in @files come into play?

    Thank you for posting your code and output. Could you please describe your input and expected behavior more? For example, I don't know if SubjectASD202 and SubjectASD203 are files or maybe directories, whether they are located in /mnt/hgfs/ExpAutism/Scripts or somewhere else, and so on. The best thing you can do, and the way you'll get help the quickest, is if you could post a Short, Self-Contained, Correct Example: something that we can just download and run and debug for ourselves.

Re: Loop is not working
by toolic (Bishop) on Oct 03, 2018 at 19:27 UTC
    If your script hangs, I suspect it is because /usr/local/fsl/bin/feat hangs (whatever that is).

    Observations:

    • You are discarding output from the backticks. For debugging purposes (to see if there is output): print `/usr/local/fsl/bin/feat Level1Run1_spec.fsf`;
    • cd .. will not change your directory. You need chdir.
    • The opendir does not really do anything in your code.
    • Customarily, the use lines appear at the top of a script.
    • $s=$s+1 is typically written as $s++
    • For other tips, refer to the Basic debugging checklist.

      Thank you so much for taking your time to comment. I used the suggestion by the user named poj. All the best.

Re: Loop is not working
by poj (Abbot) on Oct 03, 2018 at 20:17 UTC

    Consider a simple read/write loop to customise your template

    #!/usr/bin/perl use strict; use warnings; # do second level statistical analysis run by run # go to the directory where the fsf-file for stats # and post-stats is stored chdir "/mnt/hgfs/ExpAutism/Scripts"; my $infile = 'Level1Run1.fsf'; my $outfile = 'Level1Run1_spec.fsf'; my $program = '/usr/local/fsl/bin/feat'; my @subjects = ("SubjectASD202","SubjectASD203"); foreach my $subject (@subjects){ open my $fh_IN, '<', $infile or die "Could not open $infile ; $!"; open my $fh_OUT, '>', $outfile or die "Could not open $outfile ; $!"; # replace wildcards with specific titles of subjects while (<$fh_IN>){ s/Subjectx/$subject/g; print $fh_OUT $_; } close $fh_IN; close $fh_OUT; system($program,$outfile) == 0 or die "$program $outfile failed: $?"; } print "\n +++ DONE +++ \n";
    poj
      Dear poj,

      Thank you so much for your reply and precious time. I implemented your suggestion and here is the code:

      #!/usr/bin/perl use strict; use warnings; # do first level statistical analysis run by run # go to the directory where the fsf-file for stats # and post-stats is stored chdir "/mnt/hgfs/ExpAutism/Scripts"; my $infile = 'Level1Run1_test.fsf'; my $outfile = 'Level1Run1_test_spec.fsf'; my $program = '/usr/local/fsl/bin/feat'; my @subjects = ("SubjectASD203","SubjectTD106C"); foreach my $subject (@subjects){ open my $fh_IN, '<', $infile or die "Could not open $infile ; $!"; open my $fh_OUT, '>', $outfile or die "Could not open $outfile ; $!"; # replace wildcards with specific titles of subjects while (<$fh_IN>){ s/Subjectx/$subject/g; print $fh_OUT $_; } close $fh_IN; close $fh_OUT; system($program,$outfile) == 0 or die "$program $outfile failed: $?"; } print "\n +++ DONE +++ \n";

      The error message on the terminal:  C/usr/local/fsl/bin/feat Level1Run1_test_spec.fsf failed: 2 at Level1Run1_test.pl line 28. The line 28 is  system($program,$outfile) == 0

      I am confused again. The problem seems to be with the feat file but what does "2" mean there? Do you have any suggestions?

      PS1: The parent folder of each subject folder is /mnt/hgfs/ExpAutismand the parent folder of the fsf and pl files is  /mnt/hgfs/ExpAutism/Scripts

      Thanks in advance. Best,
        In general, I would not do a chdir from the Perl script and prefer to use a path name. But if you do a chdir, you should check for success.
        Did chdir  "/mnt/hgfs/ExpAutism/Scripts"; work? -> check the result of all file system operations.

        In general, I've found that changing the running default directory causes more problems than it fixes. Just a thought.

        die "$program $outfile failed: $?"; ... C/usr/local/fsl/bin/feat Level1Run1_test_spec.fsf failed: 2 at Level1Run1_test.pl ... what does "2" mean there?

        See $? and system for the interpretation of values of $?. In this case, it means that the program exited due to signal nr. 2, which normally corresponds to SIGINT, which normally corresponds to hitting Ctrl-C on your keyboard, and which would also explain the C at the beginning of your output (on my system, it shows as ^C).

        So it looks like you hit Ctrl-C while /usr/local/fsl/bin/feat was running. If so, why did you do that?

        How did "C" get pre-pended in the output:
        C/usr/local/fsl/bin/feat
        ? The assignment was :
        my $program = '/usr/local/fsl/bin/feat';
        which should have worked.

        If you need the drive letter, use "C:".

                        Memory fault   --   brain fried

        This is a huge guess here, but I feel that this is due to a #define ENOENT 2 /* No such file or directory */ that may have been encountered.

        You may be in a directory that you think you are but aren't, or possibly trying to work on a file from a location where it doesn't exist. Can you change:

        system($program,$outfile) == 0 or die "$program $outfile failed: $?";

        ...to:

        print -x $program . "\n"; open my $fh, '<', $outfile or die $!;

        ...within the exact same code and see if you get an error?

Re: Loop is not working
by jwkrahn (Abbot) on Oct 04, 2018 at 00:53 UTC

    Another problem with your code:

    for (@array) { #replace wildcards with specific titles of subjects s/Subjectx/$files[$s]/g; }

    You are using a fixed string for a regular expression which means that the first time through the loop the string "Subjectx" will be replaced so that the second time through the loop the string will not exist and nothing will be replaced.

      Thanks to everyone for commenting on my question. I just added the following line  use diagnostics; and reran the code. The loop worked successfully, and the result seems to be okay too. However, I received this long and confusing message in the terminal. What does this message mean exactly?

      sluser@localhost Scripts]$ perl Level1Run1_test.pl 1538605204768 addons.update-checker WARN onUpdateCheckComplet +e failed to parse update manifest: [Exception... "Update manifest is +missing a required addons property." nsresult: "0x80004005 (NS_ERROR +_FAILURE)" location: "JS frame :: resource://gre/modules/addons/Addo +nUpdateChecker.jsm :: getRequiredProperty :: line 473" data: no] Sta +ck trace: getRequiredProperty()@resource://gre/modules/addons/AddonUp +dateChecker.jsm:473 < parseJSONManifest()@resource://gre/modules/addo +ns/AddonUpdateChecker.jsm:483 < UpdateParser.prototype.onLoad/parser( +)@resource://gre/modules/addons/AddonUpdateChecker.jsm:655 < UpdatePa +rser.prototype.onLoad()@resource://gre/modules/addons/AddonUpdateChec +ker.jsm:675 < UpdateParser/<()@resource://gre/modules/addons/AddonUpd +ateChecker.jsm:593 1538605204784 addons.update-checker WARN onUpdateCheckComplet +e failed to parse update manifest: [Exception... "Update manifest is +missing a required addons property." nsresult: "0x80004005 (NS_ERROR +_FAILURE)" location: "JS frame :: resource://gre/modules/addons/Addo +nUpdateChecker.jsm :: getRequiredProperty :: line 473" data: no] Sta +ck trace: getRequiredProperty()@resource://gre/modules/addons/AddonUp +dateChecker.jsm:473 < parseJSONManifest()@resource://gre/modules/addo +ns/AddonUpdateChecker.jsm:483 < UpdateParser.prototype.onLoad/parser( +)@resource://gre/modules/addons/AddonUpdateChecker.jsm:655 < UpdatePa +rser.prototype.onLoad()@resource://gre/modules/addons/AddonUpdateChec +ker.jsm:675 < UpdateParser/<()@resource://gre/modules/addons/AddonUpd +ateChecker.jsm:593 1538605204810 addons.update-checker WARN onUpdateCheckComplet +e failed to parse update manifest: [Exception... "Update manifest is +missing a required addons property." nsresult: "0x80004005 (NS_ERROR +_FAILURE)" location: "JS frame :: resource://gre/modules/addons/Addo +nUpdateChecker.jsm :: getRequiredProperty :: line 473" data: no] Stack trace: getRequiredProperty()@resource://gre/modules/a +ddons/AddonUpdateChecker.jsm:473 < parseJSONManifest()@resource://gre +/modules/addons/AddonUpdateChecker.jsm:483 < UpdateParser.prototype.o +nLoad/parser()@resource://gre/modules/addons/AddonUpdateChecker.jsm:6 +55 < UpdateParser.prototype.onLoad()@resource://gre/modules/addons/Ad +donUpdateChecker.jsm:675 < UpdateParser/<()@resource://gre/modules/ad +dons/AddonUpdateChecker.jsm:593 1538605204818 addons.update-checker WARN onUpdateCheckComplet +e failed to parse update manifest: [Exception... "Update manifest is +missing a required addons property." nsresult: "0x80004005 (NS_ERROR +_FAILURE)" location: "JS frame :: resource://gre/modules/addons/Addo +nUpdateChecker.jsm :: getRequiredProperty :: line 473" data: no] Sta +ck trace: getRequiredProperty()@resource://gre/modules/addons/AddonUp +dateChecker.jsm:473 < parseJSONManifest()@resource://gre/modules/addo +ns/AddonUpdateChecker.jsm:483 < UpdateParser.prototype.onLoad/parser( +)@resource://gre/modules/addons/AddonUpdateChecker.jsm:655 < UpdatePa +rser.prototype.onLoad()@resource://gre/modules/addons/AddonUpdateChec +ker.jsm:675 < UpdateParser/<()@resource://gre/modules/addons/AddonUpd +ateChecker.jsm:593 1538605204826 addons.update-checker WARN onUpdateCheckComplet +e failed to parse update manifest: [Exception... "Update manifest is +missing a required addons property." nsresult: "0x80004005 (NS_ERROR +_FAILURE)" location: "JS frame :: resource://gre/modules/addons/Addo +nUpdateChecker.jsm :: getRequiredProperty :: line 473" data: no] Sta +ck trace: getRequiredProperty()@resource://gre/modules/addons/AddonUp +dateChecker.jsm:473 < parseJSONManifest()@resource://gre/modules/addo +ns/AddonUpdateChecker.jsm:483 < UpdateParser.prototype.onLoad/parser( +)@resource://gre/modules/addons/AddonUpdateChecker.jsm:655 < UpdatePa +rser.prototype.onLoad()@resource://gre/modules/addons/AddonUpdateChec +ker.jsm:675 < UpdateParser/<()@resource://gre/modules/addons/AddonUpd +ateChecker.jsm:593 + DONE +++

      Just in case you need to see the code I used again:

      #!/usr/bin/perl use strict; use warnings; use diagnostics; # do second level statistical analysis run by run # go to the directory where the fsf-file for stats # and post-stats is stored chdir "/mnt/hgfs/ExpAutism/Scripts"; my $infile = 'Level1Run1_test.fsf'; my $outfile = 'Level1Run1_test_spec.fsf'; my $program = '/usr/local/fsl/bin/feat'; my @subjects = ("SubjectASD203","SubjectTD106C"); foreach my $subject (@subjects){ open my $fh_IN, '<', $infile or die "Could not open $infile ; $!"; open my $fh_OUT, '>', $outfile or die "Could not open $outfile ; $!"; # replace wildcards with specific titles of subjects while (<$fh_IN>){ s/Subjectx/$subject/g; print $fh_OUT $_; } close $fh_IN; close $fh_OUT; system($program,$outfile) == 0 or die "$program $outfile failed: $?"; } print "\n +++ DONE +++ \n";

      Thanks a ton!

        Hi, that message seems to refer to an issue with your Mozilla-based browser. I don't know how this comes into play in your program; I did look at the source of feat a little bit and saw that it outputs HTML, so maybe related?

        Anyway I wanted to point you to one of many ways to capture all the output from your external command, Capture::Tiny. You could use something like this to examine the output and perhaps see where the source of the message is:

        use Capture::Tiny ':all'; my ($stdout, $stderr, $exit) = capture { system( $program, $outfile ); }; print sprintf('Stderr: >%s< Stdout: >%s<', $stderr, $stdout); if ( $exit != 0 ) { die sprintf('%s exited with status %s.', $program, $exit; }

        Hope this helps!


        The way forward always starts with a minimal test.

        I agree with 1nickt that this looks like error messages from a Mozilla browser. At the moment I don't see the version of feat that 1nickt found launching a browser though - could you point us to where you got feat and its accompanying tools?

        So in other words this isn't Perl outputting these messages, and I think you could probably just ignore them for now, as long as the rest of the code runs and the output looks good.