Синтаксическая ошибка при использовании файлового дескриптора, хранящегося в хеш-значении - PullRequest
2 голосов
/ 19 июня 2019

Я пытаюсь с помощью одной строки записать строки, считанные из входного источника, в разные файлы в соответствии с содержимым поля самой строки.

Мой метод состоял в том, чтобы использовать хеш для хранения файловых дескрипторов, чтобы избежать многократного открытия одного и того же файла, добавляя ключи к этому хешу динамически, как только значение для поля, которое не появилось ранее, было найдено в новой строке для чтения .

Однако, используя значение хеша, которое хранит файловый дескриптор с общим вызовом print / printf, таким как

print $hash{ $field_value } "String"

приводит к ошибке компиляции. Например, замена $hash{ $field_value } на STDERR приводит к чистой компиляции.

Ниже приведен пример с однострочником.

$ cat /tmp/input_file 
mail1.sql:INSERT INTO
mail2.sql:INSERT INTO
mail3.sql:INSERT INTO
mail4.sql:INSERT INTO
mail6.sql:INSERT INTO
mail7.sql:INSERT INTO
mail8.sql:INSERT INTO
mail9.sql:INSERT INTO
maildev.sql:INSERT INTO

$ perl -C -nE '
m{^(?<server>mail[^.]+)\.sql:(?<string>INSERT INTO)};
exists $fd->{$+{server}} or open($fd->{$+{server}}, ">>", "/tmp/". $+{server}.".tmp.sql");
print sprintf(qq{server %s.domain.tld command %s;\n}, $+{server}, $+{string});
' /tmp/input_file 
server mail1.domain.tld command INSERT INTO;
server mail2.domain.tld command INSERT INTO;
server mail3.domain.tld command INSERT INTO;
server mail4.domain.tld command INSERT INTO;
server mail6.domain.tld command INSERT INTO;
server mail7.domain.tld command INSERT INTO;
server mail8.domain.tld command INSERT INTO;
server mail9.domain.tld command INSERT INTO;
server maildev.domain.tld command INSERT INTO;

# The files were opened, creating them. print-ing to STDOUT works fine.

$ ls -1  /tmp/mail*sql
/tmp/mail1.tmp.sql
/tmp/mail2.tmp.sql
/tmp/mail3.tmp.sql
/tmp/mail4.tmp.sql
/tmp/mail6.tmp.sql
/tmp/mail7.tmp.sql
/tmp/mail8.tmp.sql
/tmp/mail9.tmp.sql
/tmp/maildev.tmp.sql

# Now using $fd->{$+{server}} as filehandle for print

$ perl -C -nE '
m{^(?<server>mail[^.]+)\.sql:(?<string>INSERT INTO)};
exists $fd->{$+{server}} or open($fd->{$+{server}}, ">>", "/tmp/". $+{server}.".tmp.sql");
print $fd->{$+{server}} sprintf(qq{server %s.domain.tld command %s;\n}, $+{server}, $+{string});
' /tmp/input_file
syntax error at -e line 4, near "} sprintf"
syntax error at -e line 5, near ";}"
Execution of -e aborted due to compilation errors.

# Simplifying further

$ perl -C -nE '
my $a = "test";                                      
exists $fd->{$a} or open($fd->{$a}, ">>", "/tmp/". $a.".tmp.sql");
print $fd->{$a} sprintf(qq{AAA %s AAA\n}, $a);
' /tmp/input_file
syntax error at -e line 4, near "} sprintf"
BEGIN not safe after errors--compilation aborted at -e line 4.

Я пытался сделать это, используя perl 5.22 и 5.30, просто чтобы проверить, не была ли недавно исправлена ​​какая-то ошибка.

Может быть, я упускаю что-то очевидное, но я не вижу, что.

У кого-нибудь есть идея?

Заранее спасибо.

1 Ответ

5 голосов
/ 19 июня 2019

Вам нужно обернуть ручку в пустой блок.

print { $hash{ $field_value } } "String";

Это задокументировано в print perldoc .

Если выхраните дескрипторы в массиве или хэше, или вообще, когда вы используете какое-либо выражение, более сложное, чем дескриптор голого слова или простую, неподписанную скалярную переменную, чтобы получить его, вам придется использовать блок, возвращающий значение дескриптора файла,в этом случае СПИСОК не может быть опущен:

print { $files[$i] } "stuff\n";
print { $OK ? *STDOUT : *STDERR } "stuff\n";
...