Передача аргументов системным командам из Perl
Я пишу Perl-скрипт, который использует некоторые команды ImageMagick, в частности, определить. Соответствующий код здесь:
my $height = system("identify -format %h $curPic");
my $width = system("identify -format %w $curPic");
Когда я запускаю весь скрипт, он зависает в этих строках, и это вывод:
identify: unable to open image 'if0W211.jpg': No such file or directory @ error/blob.c/OpenBlob/3323
identify: unable to open image 'if0W211.jpg': No such file or directory @ error/blob.c/OpenBlob/3323
Сначала проблема была связана с тем, что в ImageMagick отсутствовали делегаты правильного формата для работы с изображениями jpg, но после исправления я все еще получаю эту ошибку. Я не могу найти документацию об ошибках, связанных с "error/blob.c/OpenBlob/3323". После написания некоторого тестового кода, чтобы увидеть, в чем может быть проблема, я думаю, что я определил, что это как-то связано с тем, как perl передает аргументы системным командам, потому что когда я пишу эту системную команду identify -format %h xxxx.jpg
в терминале работает просто отлично. Я также заметил, что когда я print "$curPic\n
256 добавляется к имени файла во время печати. Я не знаю, почему это будет.
Для справки вот как я собираю имена файлов:
opendir DIR, $folder or die "Cannot open directory: $!";
my @files = readdir(DIR);
closedir(DIR);
И вот полный сценарий:
#!/usr/bin/perl -w
use strict;
use diagnostics;
use File::Copy;
my $folder = "/media/sf_Pictures_from_Reddit";
my $oriFolder = "/media/sf_WrongOrientation";
my $resFolder = "/media/sf_LowRes";
#Collect all the files
opendir DIR, $folder or die "Cannot open directory: $!";
my @files = readdir(DIR);
closedir(DIR);
#Iterate through each file and check its orientation and resolution
foreach my $curPic (@files) {
my $height = system("identify -format %h $curPic");
my $width = system("identify -format %w $curPic");
#move those that are vertically oriented to a different folder
if ($height >= ($width*0.8)) {
move($curPic, $oriFolder//$curPic) or die "The ori move operation failed for image $curPic: $!";
print "$curPic was not approved because of its orientation.";
next;
}
#move those that are low res to a third folder
elsif (($height < 1080) | ($width < 1920)) {
move($curPic, $resFolder//$curPic) or die "The res move operation failed for image $curPic: $!";
print "$curPic was not approved because of its resolution.";
next;
}
print "$curPic is approved as a desktop background";
}
РЕДАКТИРОВАТЬ: я переключаюсь на рекомендуемую библиотеку Image::Size, так что вот мой обновленный скрипт. Это работает какое-то время, давая мне желаемый результат, но внезапно ломается и говорит, что переменные неинициализированы. "Использовать неинициализированную переменную..." Ошибка для $height и $width содержит ошибку, но они снова происходят примерно после 20 успешных итераций. И, кажется, все меняется, если я запускаю скрипт несколько раз подряд.
#!/usr/bin/perl -w
use strict;
use diagnostics;
use File::Copy;
use Image::Size;
my $folder = "/media/sf_Pictures_from_Reddit";
my $oriFolder = "/media/sf_WrongOrientation";
my $resFolder = "/media/sf_LowRes";
my $height = 0;
my $width = 0;
#Collect all the files
opendir DIR, $folder or die "Cannot open directory: $!";
my @files = readdir(DIR);
closedir(DIR);
#Iterate through each file and check its orientation and resolution
foreach my $curPic (@files) {
($width, $height) = imgsize("$folder/$curPic");
#move those that are vertically oriented to a different folder
if ($height >= ($width*0.8)) {
move("$folder/$curPic", "$oriFolder/$curPic") or die "The ori move operation failed for image $curPic: $!";
print "$curPic was not approved because of its orientation.\n";
next;
}
#move those that are low res to a third folder
elsif (($height < 1080) | ($width < 1920)) {
move("$folder/$curPic", "$resFolder/$curPic") or die "The res move operation failed for image $curPic: $!";
print "$curPic was not approved because of its resolution.\n";
next;
}
print "$curPic is approved as a desktop background.\n";
}
3 ответа
Как говорится в сообщении, вы передаете путь к несуществующему файлу. Вместо прохождения
if0W211.jpg
ты должен пройти
/media/sf_Pictures_from_Reddit/if0W211.jpg
Это все еще оставляет вас со следующими проблемами:
- Инъекционная ошибка
- Возможна неправильная интерпретация пути в качестве опции.
- Отсутствие обработки ошибок
- Отсутствие захвата вывода программы.
- Повторные исполнения внешнего процесса.
Все это можно исправить, используя вместо этого Image:: Size.
Но если вы настаиваете на использовании identify
,
use IPC::System::Simple qw( capturex );
my $dimensions = eval { capturex("identify", "-format", "%h,%w", "--", "$folder/$curPic") }
or do {
warn("Can't determine the dimensions of \"$folder/$curPic\": $@");
next;
};
my ($height, $width) = $dimensions =~ /^(\d+),(\d+)$/
or do {
warn("Can't determine the dimensions of \"$folder/$curPic\": Unexpected output from \"identify\": $dimensions\n");
next;
};
Если твой identify
не поддерживает --
(или даже если это так), вы можете заменить
"--", "$folder/$curPic"
с
"$folder/$curPic" =~ s{^-}{./-}r
В существующем состоянии вы создаете два новых процесса (один для определения ширины, а другой для определения высоты) для каждого изображения, что потенциально может вызвать значительную нагрузку на вашу систему, если у вас есть большое количество изображений.
В качестве альтернативы вы можете просто вызвать один identify
обработайте и передайте все имена изображений, например:
identify -format "%w:%h:%f\n" *jpg
Пример вывода
2000:1200:ok.jpg
1200:2000:toolow2.jpg
1000:500:vert.jpg
Затем вы можете разобрать это с bash
или же Perl
:
#!/bin/bash
VERT="/tmp"
TOOLOW="/tmp"
identify -format "%w:%h:%f\n" *jpg |
while IFS=: read w h name; do
echo "DEBUG: $name, $w, $h"
[[ $h -gt $(( (8*w)/10 )) ]] && { mv "$name" "$VERT"; >&2 echo "Too tall: $name"; continue; }
[[ ($h -lt 1080) || ($w -lt 1920) ]] && { mv "$name" "$TOOLOW"; >&2 echo "Low res: $name"; continue; }
echo "$name approved"
done
Вот последний скрипт, который работает для моих целей. Я добавил проверки определений и ненулевые проверки, чтобы убедиться, что переменные получают правильный ввод, прежде чем двигаться вперед.
#!/usr/bin/perl -w
use strict;
use diagnostics;
use File::Copy;
use Image::Size;
my $folder = "/media/sf_Pictures_from_Reddit";
my $oriFolder = "/media/sf_WrongOrientation";
my $resFolder = "/media/sf_LowRes";
my $height = 0;
my $width = 0;
#Collect all the files
opendir DIR, $folder or die "Cannot open directory: $!";
my @files = readdir(DIR);
closedir(DIR);
#Iterate through each file and check its orientation and resolution
foreach my $curPic (@files) {
($width, $height) = imgsize("$folder/$curPic") or die "Couldn't get the image dimensions for $curPic: $!";
if($curPic eq "." || $curPic eq ".."){ next;}
if((defined $height) & (defined $width)){
if(($height != 0) & ($width != 0)) {
print "File: $curPic\nHeight: $height\nWidth: $width\n";
#sleep(0.5);
#move those that are vertically oriented to a different folder
if($height >= ($width*0.8)) {
move("$folder/$curPic", "$oriFolder/$curPic") or die "The ori move operation failed for $curPic: $!";
print "$curPic was not approved because of its orientation.\n\n";
next;
}
#move those that are low res to a third folder
elsif(($height < 1080) | ($width < 1920)) {
move("$folder/$curPic", "$resFolder/$curPic") or die "The res move operation failed for $curPic: $!";
print "$curPic was not approved because of its resolution.\n\n";
next;
}
print "$curPic is approved as a desktop background.\n\n";
$height = 0;
$width = 0;
}
else{
print "Variables failed to initialize.\n\n";
}
}
else{
print "Variables are undefined.\n\n";
}
}