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

A question was asked a few days ago about online translations, language translate, where OP seemed more concerned about asking the question than getting results and writing them up legibly. I've been working on the same problem, having grown weary of all the GUI events involved in pasting text in and out of online translators. I wanted to post what what I have to address a warning and to share my experience with a wonderful tool.

Being unable to figure out what all is involved in getting a google API key, I found something else that works as a translate shell that I can manipulate using perl, and without cost. soimort translate shell was easy to install and has given me functionality that I lacked using google alone. Just today, I wrote code to get translations from 3 other translation sites. I'm gonna put abridged output, the driver .pl, and the subroutine for creating russian captions between readmore tags, then pull out lines to discuss thereafter.

$ ./1.keyboard1.pl title is 1.keyboard path1 is /home/bob/2.scripts/pages/1.keyboard abs is /home/bob/2.scripts/pages/1.keyboard/1.keyboard1.pl variable is &#1087;&#1077;&#1088;&#1077;&#1084;&#1077;&#1085;&#1085;&# +1072;&#1103; &#1087;&#1077;&#1088;&#1077;&#1084;&#1077;&#1085;&#1085;&#1072;&#1103; + is variable ini path is ...internals... object created, back with caller word is 1.keyboard dir2 is perlmonks 1.keyboard1.html 1.keyboard2.html 1.keyboard3.html files are 1.keyboard1.html 1.keyboard2.html 1.keyboard3.html old num is 3 Make rus captions(y/n)?: y matching are a.txt b.txt c.txt rus_munge is /home/bob/2.scripts/pages/1.keyboard/template_stuff/trans +lations/trans.02-11-2018-16-14-09.txt rus_path is /home/bob/2.scripts/pages/1.keyboard/template_stuff/ruscap +tions/a.txt rus_path is /home/bob/2.scripts/pages/1.keyboard/template_stuff/ruscap +tions/b.txt rus_path is /home/bob/2.scripts/pages/1.keyboard/template_stuff/ruscap +tions/c.txt Get other translations(y/n)?: y getting translation from yandex getting translation from bing getting translation from apertium remote_dir is 1.keyboard4 result is <!DOCTYPE html> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="/css/1.keyboard1.css"/> <title>1.keyboard</title> </head> <body> <div class="wrapper"> <div class = origin> <h3>Vancouver, Fri Nov 2 16:14:53 2018</h3> </div> <h1>qwerty: &#1081;&#1094;&#1091;&#1082;&#1077;&#1085;</h1> dir is /home/bob/2.scripts/pages/1.keyboard/template_stuff/captions dir is /home/bob/2.scripts/pages/1.keyboard/template_stuff/ruscaptions tmpl is /home/bob/2.scripts/pages/1.keyboard/template_stuff/code2.tmpl Put file to server(y/n)?: y server dir is perlmonks mkdir1 failed path3 is /home/bob/2.scripts/pages/1.keyboard/template_stuff/1.keyboar +d1.css mkdir2 failed /pmimage/1.keyboard4 ... return is 1.keyboard4.html $ $ cat 1.keyboard1.pl #!/usr/bin/perl -w use 5.011; use lib "template_stuff"; use html7; use trans1; use Path::Tiny; use utf8; use Encode; use open OUT => ':encoding(UTF-8)', ':std'; # initializations that must precede main data structure my $ts = "template_stuff"; my $images = "aimages"; my $captions = "captions"; my $ruscaptions = "ruscaptions"; ## turning things to Path::Tiny # decode paths my $abs = path(__FILE__)->absolute; my $path1 = Path::Tiny->cwd; my $title = $path1->basename; $abs = decode('UTF-8', $abs); $path1 = decode('UTF-8', $path1); $title = decode('UTF-8', $title); say "title is $title"; say "path1 is $path1"; say "abs is $abs"; my $path2 = path( $path1, $ts ); # page params my %vars = ( title => $title, headline => undef, place => 'Vancouver', base_url => 'http://www.merrillpjensen.com', css_file => "${title}1.css", header => path( $path2, "hc_input2.txt" ), footer => path( $path2, "footer_center3.txt" ), body => path( $path2, "rebus5.tmpl" ), print_script => "1", code_tmpl => path( $path2, "code2.tmpl" ), oitop => path( $path2, "oitop.txt" ), oibottom => path( $path2, "oibottom.txt" ), to_images => path( $path2, $images ), eng_captions => path( $path2, $captions ), rus_captions => path( $path2, $ruscaptions ), translations => path( $path2, 'translations' ), bottom => path( $path2, "bottom1.txt" ), book => 'qwerty: ', chapter => '&#1081;&#1094;&#1091;&#1082;&#1077;&#1085;', print_module => 0, script_file => $abs, module_tmpl => path( $path2, "code3.tmpl" ), server_dir => 'perlmonks', image_dir => 'pmimage', ts => 'template_system', css_path => $path2, ); my $variable = "&#1087;&#1077;&#1088;&#1077;&#1084;&#1077;&#1085;&#108 +5;&#1072;&#1103;"; say "variable is $variable"; my $&#1087;&#1077;&#1088;&#1077;&#1084;&#1077;&#1085;&#1085;&#1072;&#1 +103; = "variable"; say "&#1087;&#1077;&#1088;&#1077;&#1084;&#1077;&#1085;&#1085;&#1072;&# +1103; is $&#1087;&#1077;&#1088;&#1077;&#1084;&#1077;&#1085;&#1085;&#1 +072;&#1103;"; my $rvars = \%vars; my $return = create_page( $rvars ); say "return is $return"; __END__ $ cd template_stuff/ $ cat trans1.pm package trans1; require Exporter; use utils1; our @ISA = qw(Exporter); our @EXPORT = qw( get_tiny make_russian_captions ); sub make_russian_captions { use 5.011; use warnings; use POSIX qw(strftime); use Path::Tiny; use Encode; use open OUT => ':encoding(UTF-8)', ':std'; my $rvars = shift; my %vars = %$rvars; my $munge = strftime( "%d-%m-%Y-%H-%M-%S\.txt", localtime ); my $in_path = path( $vars{translations}, $munge )->touchpath; my $lang = 'ru'; #system("pwd >$in_path"); works my @matching2; opendir( my $hh, $vars{eng_captions} ) or die "death $!\n"; while ( defined( $_ = readdir($hh) ) ) { if (m/txt$/) { push( @matching2, $_ ); } } #important to sort @matching2 = sort @matching2; say "matching are @matching2"; my $rus_munge = path( $vars{translations}, "trans." . $munge ); say "rus_munge is $rus_munge"; # open file for writing my $fh = path($in_path)->openw_utf8; foreach (@matching2) { my $eng_path = path( $vars{eng_captions}, $_ ); say $fh "##$_##"; my $rus_path = path( $vars{rus_captions}, $_ )->touchpath; say "rus_path is $rus_path"; my $content = path($eng_path)->slurp_utf8; $content =~ s/^\s+|\s+$//g; say $fh "$content"; system("trans :$lang file://$eng_path >$rus_path"); } print "Get other translations(y/n)?: "; my $prompt = <STDIN>; chomp $prompt; if ( $prompt eq ( "y" | "Y" ) ) { my @translators = qw /yandex bing apertium /; for my $remote (@translators) { my $trans_munge = path( $vars{translations}, "$remote." . $munge + ); ## use trans shell say "getting translation from $remote"; system("trans :$lang -e $remote file://$in_path >$trans_munge"); } } return "nothing yet"; } sub get_tiny { ...elided... } 1; $

To start with, I think I figured out what use utf8; entails with this code:

my $variable = "переменная";
say "variable is $variable";

my $переменная = "variable";
say "переменная is $переменная";

This gives

variable is переменная
переменная is variable

, but with use utf8; commented out, I get:

$ ./1.keyboard1.pl 
Can't use global $Ð in "my" at ./1.keyboard1.pl line 65, near "my $Ð"
Unrecognized character \xBF; marked by <-- HERE after my $Ð<-- HERE near column 6 at ./1.keyboard1.pl line 65.
$ 

Line 65 is the second declaration, so you can have exotic utf8 values without use utf8;, but not variable names. (More an observation than a question.)

$sftp->mkdir("/$server_dir") or warn "mkdir1 failed $!\n"; $sftp->mkdir("/$image_dir") or warn "mkdir2 failed $!\n";

Now for the questions. I'm looking for a way to make the failure of mkdir more informative for the case that the directory already exists. I keep the mkdir line so that it will work the first time it is used. Every subsequent time, it fails, and $! is stone silent. This module is on github at html7.pm.

This is supposedly the list of engines for use, but I have found that spell, aspell, and apertium simply fail, and deepl gives me instead what looks to be a good french translation. I have google make my default captions, and then consult what bing and yandex have done. For what I have seen, if you're making russian translations, yandex might be the best.

$ trans -S aspell * google deepl bing spell apertium yandex $

The functionality I would like to add are conditions such that if a translate call hangs, I can get to the next one, so what I'm fishing for is code that would go the next in the for loop if it lasts for, say, a minute.

print "Get other translations(y/n)?: "; my $prompt = <STDIN>; chomp $prompt; if ( $prompt eq ( "y" | "Y" ) ) { my @translators = qw /yandex bing apertium /; for my $remote (@translators) { my $trans_munge = path( $vars{translations}, "$remote." . $munge + ); ## use trans shell say "getting translation from $remote"; system("trans :$lang -e $remote file://$in_path >$trans_munge"); }

Finally, I leave some examples of usage for those interested enough to delve into readmore tags. It shows how different engines can be used, and how simple it is to re-translate phrases and see what I call "the synonym cloud," which I find critical in getting the "flavor" of words. What translators will do with strings like "Windows", "plug and play," Gedit, Georgia is idiomatic, and you can't hold it against a machine that it *isn't* supposed to translate certain words. Example of output here figuring out how to work my new cyrillic keyboard.

$ trans -e yandex :ru dog
dog

собака
(sobaka)

 English -> &#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081; 
$ trans -e Bing :ru dog
dog

Собака

 English -> &#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081; 
$ trans снимок
снимок
(snimok)

snapshot

Definitions of снимок
 &#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081; -> English 

noun
    snapshot
        снимок, фотография, фотокарточка, выстрел, выстрел навскидку

снимок
    snapshot

$ trans :ru "voter suppression"
voter suppression

голосовать за удаление
(golosovat' za udaleniye)

Translations of voter suppression
 Français -> &#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081; 

voter suppression
    голосовать за удаление, подавление голосов
$ trans голосовать за удаление
голосовать
(golosovat')

vote

Definitions of голосовать
 &#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081; -> English 

verb
    vote
        голосовать, вносить предложение, баллотировать, ассигновать, признавать, выделять
    poll
        голосовать, проводить голосование, подрезать верхушку, подсчитывать голоса, получать голоса, стричь волосы
    ballot
        голосовать, баллотировать, тянуть жребий
    vote by ballot
        голосовать
    take a ballot
        голосовать
    elect by ballot
        голосовать
    divide
        делить, разделять, делиться, разбивать, разделяться, голосовать
    poll one's vote
        голосовать

phrase
    cast a vote
        голосовать, подавать голос

голосовать
    vote, to vote
за
(za)

for

Definitions of за
 &#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080; -> English 

preposition
    for
        за, на, по, в продължение на, от, с
...
    for one's sake
        за

prefix
    pro-
        про-, в полза на, за

за
    for
удаление
(udaleniye)

deletion

Definitions of удаление
 &#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081; -> English 

noun
    remove
        удаление, шаг, колено, ступень, степень отдаления, стадия
    removal
        удаление, устранение, отстранение, перемещение, снос, смещение
    disposal
        удаление, распоряжение, размещение, избавление, продажа, устранение
    deletion
        удаление, стирание, вычеркивание, вымарывание, вымарка, то, что вычеркнуто
    excision
        удаление, иссечение, вырезание, вырезка, отрезание, вырезывание
    ablation
        удаление, устранение, таяние ледников, вырезывание, размывание пород
    withdrawal
        изъятие, отход, удаление, вывод войск, увод, отозвание
    resection
        резекция, удаление, обратная засечка, вырезание, устранение
    extirpation
        экстирпация, удаление, искоренение, истребление, вылущение, выведение



    deletion, removal
$ 
  

Thank you for your comments,

Replies are listed 'Best First'.
Re: using online translation engines with perl
by Anonymous Monk on Nov 05, 2018 at 10:50 UTC
    you can have exotic utf8 values without use utf8;, but not variable names.
    Note that the "exotic" (non-ASCII) values would be slightly different in those cases. If you don't use utf8;, you get a string scalar consisting of exactly the bytes that happened to be in the file you saved. (They can mean some text in UTF-8, or KOI8-R, or Shift JIS or nothing at all.) If you do use utf8;, Perl automatically decodes the string you'd typed from UTF-8 into Unicode characters. You can notice the difference if you use Dumper or length on the strings:
    # Dumper prints character codes for wide characters
    $ perl -MData::Dumper -Mutf8 -E'print Dumper "привет"'
    $VAR1 = "\x{43f}\x{440}\x{438}\x{432}\x{435}\x{442}";
    # Dumper prints bytes, and I get exactly what I'd typed
    $ perl -MData::Dumper -E'print Dumper "привет"'
    $VAR1 = 'привет';
    # byte-string consists of 12 bytes of UTF-8
    $ perl -MData::Dumper -E'say length "привет"'
    12
    # character-string consists of 6 wide characters
    $ perl -MData::Dumper -Mutf8 -E'say length "привет"'
    6
    
    (Using <pre> because PM engine encodes all non-ASCII characters into HTML entities and <code> doesn't let them be interpreted.)
    Every subsequent time, it fails, and $! is stone silent.
    Documentation examples show that you are supposed to look for the error in $sftp->error, not $!.
    The functionality I would like to add are conditions such that if a translate call hangs, I can get to the next one, so what I'm fishing for is code that would go the next in the for loop if it lasts for, say, a minute.
    For a really primitive timeout implementation, see alarm. If you need more fine-grained control, threads with Thread::Queue might be a good solution.

      Thanks for elaborating on utf8 and suggesting on a way forward with my questions. As is frequently the case, the monastery has another thread going to discuss the same issue: Can we write multi-threaded scripts ?. I looked at the reference that localshop posted and found code that uses perl's native language to thread. I would prefer not to have to use a module just for this little task. I must say, however that I fall short on my first attempt in some ways. Output then source:

      $ ./2.fork.pl 
      this is task 1 with pid 0
      Waiting for child processes..
      this is task 2 with pid 0
      this is task 3 with pid 0
      44
      bing's translation is 
      unctuous hypocrisy flowing from the tube
      
      елейным лицемерие течет из трубки
      
       English -> &#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081; 
      Child with PID=23085 finished..
      43
      42
      41
      slept 5
      40
      39
      38
      ERROR Oops! Something went wrong and I can't translate it for you :(
      yandex's translation is 
      Child with PID=23086 finished..
      37
      36
      slept 10
      35
      google's translation is 
      unctuous hypocrisy flowing from the tube
      
      неприступное лицемерие, вытекающее из трубки
      (nepristupnoye litsemeriye, vytekayushcheye iz trubki)
      
      Translations of unctuous hypocrisy flowing from the tube
       English -> &#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081; 
      
      unctuous hypocrisy flowing from the tube
          неприступное лицемерие, вытекающее из трубки, елейное лицемерие течет из трубки
      Child with PID=23087 finished..
      Done.
      $ cat 2.fork.pl 
      #!/usr/bin/perl -w
      use 5.011;
      
      my $string = "unctuous hypocrisy flowing from the tube";
      
      my $pid1 = fork();
      if ( $pid1 == 0 ) {    # Task 1
          say "this is task 1 with pid $pid1";
          my $command = "trans :ru -e bing \"$string\" >1.bing.txt"; 
          system("$command");
          say "bing's translation is ";
          system("cat 1.bing.txt");
          exit 0;
      }
      
      my $pid2 = fork();
      if ( $pid2 == 0 ) {    # Task 2
          say "this is task 2 with pid $pid2";
          sleep 5;
          say "slept 5";
          system("trans :ru -e yandex \"$string\" >1.yandex.txt");
          say "yandex's translation is ";
          system("cat 1.yandex.txt");
          exit 0;
      }
      
      my $pid3 = fork();
      if ( $pid3 == 0 ) {    # Task 3
          say "this is task 3 with pid $pid3";
          sleep 10;
          say "slept 10";
          system("trans :ru -e google \"$string\" >1.google.txt");
          say "google's translation is ";
          system("cat 1.google.txt");
          exit 0;
      }
      
      say "Waiting for child processes..";
      my $counter = 45;
      local $SIG{ALRM} = sub {
          say --$counter;
          alarm 1;
      };
      alarm 1;
      
      while ((my $pid = wait) != -1) {
          say "Child with PID=$pid finished..";
      }
      
      alarm 0;
      say "Done.";
      __END__
      

      Even with all the say statements dropped in, I find execution hard to follow. I'm baffled that pid's are zero within a block, but not so when they finish. What alarm truly does here is unclear. Finally, there is no code to kill pid's when the timer reaches zero.

      I'm also fishing for code that would do this within the following loop:

      print "Get other translations(y/n)?: "; my $prompt = <STDIN>; chomp $prompt; if ( $prompt eq ( "y" | "Y" ) ) { my @translators = qw /yandex bing/; for my $remote (@translators) { my $trans_munge = path( $vars{translations}, "$remote." . $munge + ); ## use trans shell say "getting translation from $remote"; system("trans :$lang -e $remote file://$in_path >$trans_munge"); }

      Again, thanks for the very helpful comments.

        I'm baffled that pid's are zero within a block, but not so when they finish.

        fork causes the execution of a process to split into two different processes, sharing the initial memory layout but not the following changes to the variables. To help the program discern which of the two it has become, the newly produced ("child") process receives 0 as the return value of fork() and the old one ("parent") gets returned the PID of the new process. This is why you had to put an exit 0; at the end of the if ($pid == 0) block: otherwise both the parent and the child would continue executing the same parts of the program, causing a lot of confusion.

        wait returns the real (former) PIDs of the freshly terminated child processes. If the child wanted for some reason to know its PID, it could have used the $$ variable.

        This example might help:

        if (fork() == 0) { system("sh", "-c", "sleep 3 # getting yandex translation"); exit(0); } if (fork() == 0) { system("sh", "-c", "sleep 2; # getting google translation"); exit(0); } system("pstree -Apal $$"); # pstree gets the PID of the parent process + as its argument while((my $pid = wait()) != -1) { say "$pid terminated" } __END__ perl,29173 test.pl |-perl,29174 test.pl | `-sh,29177 -c sleep 3 # getting yandex translation | `-sleep,29179 3 |-perl,29175 test.pl | `-sh,29178 -c sleep 2; # getting google translation `-pstree,29176 -Apal 29173 29175 terminated 29174 terminated
        pstree command draws all child processes of the main Perl process (including itself) with their command line arguments. The Perl processes having the same command line arguments but different PIDs are the copies created by fork().
        What alarm truly does here is unclear. Finally, there is no code to kill pid's when the timer reaches zero.
        alarm arranges for a SIGALRM signal to be delivered to the calling process in the number of seconds specified as its argument. It behaves as if something else kills the process which had called alarm and forgot to disarm it. The good part is that one can use $SIG{ALRM} to trap the signal and do something meaningful instead of dying, but in your case the work is already being done in a child process, and we can let it be killed:
        my $start = time; if (fork() == 0) { # arm the alarm clock alarm(10); # create a child process that sleeps system("sleep 365d"); exit(0); } system("pstree -Apal $$"); while((my $pid = wait()) != -1) { say "$pid terminated" } say time-$start, " seconds elapsed instead of a year" __END__ perl,29620 |-perl,29621 | `-sleep,29623 365d `-pstree,29622 -Apal 29620 29621 terminated 10 seconds elapsed instead of a year
Re: using online translation engines with perl
by roboticus (Chancellor) on Nov 06, 2018 at 14:43 UTC

    Aldebaran:

    Regarding part of your your question "I'm looking for a way to make the failure of mkdir more informative for the case that the directory already exists. I keep the mkdir line so that it will work the first time it is used. Every subsequent time, it fails, and $! is stone silent.", I'd suggest creating a function that checks whether the directory exists before creating it, something like (untested):

    sub createDir { my $dirName = shift; if (-e $dirName) { if (-d $dirName) { return "Directory $dirName already exists!"; } else { return "Can't create $dirName because there's a file in th +e way!"; } } mkdir $dirName or return $!; }

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      I'd suggest creating a function that checks whether the directory exists before creating it

      sub createDir { my $dirName = shift; if (-e $dirName) { if (-d $dirName) { return "Directory $dirName already exists!"; } else { return "Can't create $dirName because there's a file in th +e way!"; } } mkdir $dirName or return $!; }

      No. Just no. Or in more words: TOCTTOU.

      The only sane way to do this is to run mkdir and have it fail in case the target already exists. This way, the operating system guarantees that there won't be a race.

      mkdir $path or die "Can't mkdir $path: $!";

      It will fail with EEXISTS at least on Unix and Windows if the target already exists. There are several more possible errors, e.g. lack of permissions, filesystem problems. See POSIX.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

        Thx for your response, alexander, I rewrote the function to reflect $sftp->error as a first best way to get useful error info from Net::SFTP::Foreign. As I started to test the new create directory, I did discover that having a plain file as the directory one wants to create causes an error, but I have played enough with this race condition that I don't want to play anymore. I'm reminded of John van Neumann's adage: "Don't play bad logic games."

        I also made a first attempt at handling the error. One is left to ponder how an sftp method call to mkdir might fail and what a person with perl can do about it. It's common for the wireless to give out, as the laptop roams with me at tortoise speed. It's a hard thing to try to induce at a critical moment against the swiftness of machine instructions.

        I'm going to revert to an architecture where I deal with the consequences of the mkdir as the critical path. I tried to write a code to remove the file, and wanted to try to do some other things if I lose my sftp connection. It's a big mess. I also tried to limit processes to reasonable amounts of time, but I haven't figured out how to handle $SIG{ALRM} yet, as none of these processes terminates. I'm gonna put abridged output and then source between readmore tags and pull out a couple issues.

        Let me just ask: do people ping anymore, or is that an old-fashioned way to see if one has internet connectivity? For example, I pinged perlmonks and got no answer, and had my terminal tied up. Might a person use curl instead?

        What would I have to add for any of those alarms to terminate the process in which they are set?

        Thanks all for comments

      Thx roboticus, people might have different needs for their mkdir capability. Given that I'm the only person running this script, I don't see how this can create a race condition.

      #load html file to server my $server_dir = $vars{"server_dir"}; say "server dir is $server_dir"; my $return1 = createDir( $server_dir, $sftp ); say "return1 is $return1"; $sftp->setcwd("/$server_dir") or warn "setcwd1 failed $!\n"; $sftp->put( $vars{html_file} ) or die "html put failed $!\n"; #load css file to server $sftp->setcwd("/css") or warn "setcwd2 failed $@\n"; my $path3 = path( $vars{css_path}, $vars{"css_file"} ); say "path3 is $path3"; my $remote_css = $vars{"css_file"}; $sftp->put( "$path3", $remote_css ) or warn "css put failed $@\n"; # upload images my $image_dir = $vars{"image_dir"}; say "image dir is $image_dir"; my $return2 = createDir( $image_dir, $sftp ); say "return2 is $return2"; $sftp->setcwd("/$image_dir") or warn "setcwd2 failed $!\n"; my $return3 = createDir( $vars{remote_dir}, $sftp ); say "return3 is $return3"; $sftp->setcwd( $vars{remote_dir} ) or warn "setcwd3 failed $!\n"; print $sftp->cwd(), "\n";

      I had to make the logic work for Net::SFTP::Foreign:

      sub createDir { use 5.011; use Net::SFTP::Foreign; my ( $dirName, $sftp ) = @_; if ( $sftp->test_e($dirName)) { if ( $sftp->test_d($dirName)) { say "execution was here"; return "Directory $dirName already exists!"; } else { return "Can't create $dirName because there's a file in the way! +"; } } my $success = $sftp->mkdir($dirName) or return $!; return $success; }

      Output:

      Put file to server(y/n)?: y server dir is perlmonks execution was here return1 is Directory perlmonks already exists! path3 is /home/bob/2.scripts/pages/1.timeout/template_stuff/1.timeout1 +.css image dir is pmimage execution was here return2 is Directory pmimage already exists! return3 is 1 /pmimage/1.timeout6

      I think that's got it. Thank you.

Re: using online translation engines with perl
by afoken (Chancellor) on Nov 08, 2018 at 20:49 UTC
    $sftp->mkdir("/$server_dir") or warn "mkdir1 failed $!\n"; $sftp->mkdir("/$image_dir") or warn "mkdir2 failed $!\n";

    Now for the questions. I'm looking for a way to make the failure of mkdir more informative for the case that the directory already exists. I keep the mkdir line so that it will work the first time it is used. Every subsequent time, it fails, and $! is stone silent. This module is on github at html7.pm.

    The mkdir you are using here is not the build-in mkdir. The latter sets $! on error (and perhaps $^E). The former one is a method of some SFTP object that could do the same, but probably has a different error mechanism.

    The code on github currently uses Net::SFTP::Foreign, and as already explained, it requires to check $sftp->error() instead of $!. You could also get rid of manual error checking by using autodie mode, much like in DBI.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)