Domanda

In una versione precedente del nostro codice, abbiamo chiamato fuori da Perl per fare una ricerca LDAP come segue:

# Pass the base DN in via the ldapsearch-specific environment variable 
# (rather than as the "-b" paramater) to avoid problems of shell 
# interpretation of special characters in the DN.
$ENV{LDAP_BASEDN} = $ldn;

$lcmd = "ldapsearch -x -T -1 -h $gLdapServer" .
        <snip>
        " > $lworkfile 2>&1";
system($lcmd);

if (($? != 0) || (! -e "$lworkfile"))
{
  # Handle the error
}

Il codice di cui sopra si tradurrebbe in una ricerca LDAP di successo, e l'output di tale ricerca sarebbe nel file $lworkfile.

Purtroppo, abbiamo recentemente riconfigurato OpenLDAP su questo server in modo che una "base DC =" è specificato in /etc/openldap/ldap.conf e /etc/ldap.conf. Questo cambiamento sembra ldapsearch media ignora la variabile d'ambiente LDAP_BASEDN, e quindi il mio ldapsearch fallisce.

Ho provato un paio di correzioni diverse, ma senza successo finora:

(1) ho cercato di tornare a utilizzare l'argomento "-b" per ldapsearch, ma sfuggire i metacaratteri della shell. Ho iniziato a scrivere il codice in fuga:

my $ldn_escaped = $ldn;
$ldn_escaped =~ s/\/\\/g;
$ldn_escaped =~ s/`/\`/g;
$ldn_escaped =~ s/$/\$/g;
$ldn_escaped =~ s/"/\"/g;

che ha generato alcuni errori Perl perché non ho sfuggito quelle espressioni regolari correttamente in Perl (il numero di riga corrisponde alla regex con gli apici inversi a).

Backticks found where operator expected at /tmp/mycommand line 404, at end of line

Allo stesso tempo, ho iniziato a dubitare questo approccio e cercato una migliore.

(2) Poi ho visto alcune domande StackOverflow ( qui e qui ), che ha suggerito un soluzione migliore.

Ecco il codice:

print("Processing...");

# Pass the arguments to ldapsearch by invoking open() with an array.
# This ensures the shell does NOT interpret shell metacharacters.
my(@cmd_args) = ("-x", "-T", "-1", "-h", "$gLdapPool",
                 "-b", "$ldn",
                 <snip>
                );

$lcmd = "ldapsearch";

open my $lldap_output, "-|", $lcmd, @cmd_args;

while (my $lline = <$lldap_output>)
{
  # I can parse the contents of my file fine
}

$lldap_output->close;

I due problemi che sto avendo con l'approccio (2) sono le seguenti:

a) Calling aperto o un sistema con una serie di argomenti non lasciarmi passare > $lworkfile 2>&1 al comando, quindi non posso fermare l'uscita ldapsearch di essere inviato a schermo, che rende il mio sguardo uscita brutto:

Processing...ldap_bind: Success (0)
        additional info: Success

b) Io non riesco a capire come scegliere quale posizione (cioè percorso e il nome del file) per l'handle di file passato a open, vale a dire non so dove $lldap_output è. Posso spostare / rinominarlo, o controllarla per scoprire dove è (o non è in realtà salvato su disco)?

Sulla base dei problemi con (2), questo mi fa pensare che dovrei tornare al metodo (1), ma non sono del tutto sicuro di come

È stato utile?

Soluzione

Un approccio potrebbe essere quello di utilizzare IPC::Open3 per consentire al codice Perl per gestire sia la stdout e stderr torrenti del programma esterno.

Altri suggerimenti

IPC :: Run3 per questo. Questo è molto simile all'approccio open '-|', ma consente di reindirizzare STDERR troppo.

Nota: $lldap_output è un tubo lettura da ldapsearch. Non c'è nessun file da creare sul disco.

Se si desidera un file su disco, è possibile utilizzare IPC :: Run3 in questo modo:

use IPC::Run3;

my ($lcmd, @cmd_args) = ... # same as approach (2) above
my $lworkfile         = ... # same as approach (1) above

run3 [ $lcmd, @cmd_args ], undef, $lworkfile, $lworkfile;

Questo è come approccio (1), ma utilizzando al posto di -b $ENV{LDAP_BASEDN}.

Grazie a Greg Hewgill per la risposta. Sto postando il mio codice qui sotto nel caso aiuta chiunque altro che vogliono utilizzare la funzione Open3.

use File::Copy;
use IPC::Open3;

# Pass the arguments to ldapsearch by invoking open() with an array.
# This ensures the shell does NOT interpret shell metacharacters.
my(@cmd_args) = ("-x", "-T", "-1", "-h", "$gLdapPool",
                 "-b", "$ldn",
                 <snip>
                );
$lcmd = "ldapsearch";
my $lldap_output;

# First arg is undef as I don't need to pass any extra input to the 
# process after it starts running.
my $pid = open3(undef, $lldap_output, $lldap_output, $lcmd, @cmd_args);

# Wait for the process to complete and then inspect the return code.
waitpid($pid, 0);

my $ldap_retcode = $? >> 8;

if ($ldap_retcode != 0)
{
  # Handle error
}

# Copy the output to $lworkfile so I can refer to it later if needed       
copy($lldap_output, $lworkfile);

while (my $lline = <$lldap_output>)
{
  # I can parse the contents of my file fine
}

$lldap_output->close;

Vedere la documentazione per aperta . È possibile duplicare e reindirizzare STDERR, eseguire il comando, quindi ripristinare STDERR. E 'più dettagliato rispetto all'utilizzo di una delle IPC: :( Open3, Esegui, Run3, etc.) le biblioteche, ma possibile fare a meno di loro se non si può / si non installare moduli aggiuntivi, o non si desidera utilizzare IPC :: Open3.

Ecco un modo hacky di leggere sia STDOUT e STDERR da un programma esterno con più argomenti utilizzando pianura ol' 'aperto':

my @command_with_arguments = (YOUR_PROGRAM, ARG1, ARG2, ARG3);
foreach(@command_with_arguments){s/'/'"'"'/g;}
foreach(@command_with_arguments){s/(.+)/'$1'/;}
my $run_command = join (' ', @command_with_arguments) . " 2>&1 |";
open my $program_output, $run_command;

Ora basta leggere $ program_output per ottenere sia STDOUT e STDERR.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top