¿Cómo obtengo la ruta completa a un script de Perl que se está ejecutando?

168

Tengo el script Perl y necesito determinar la ruta completa y el nombre del archivo durante la ejecución. Descubrí que, dependiendo de cómo llames, el script $0varía y, a veces, contiene el fullpath+filenamey, a veces, solo filename. Debido a que el directorio de trabajo también puede variar, no puedo pensar en una forma de obtener de manera confiable fullpath+filenameel script.

¿Alguien tiene una solución?

Chris Madden
fuente

Respuestas:

251

Hay algunas maneras:

  • $0 es el script actualmente en ejecución según lo provisto por POSIX, en relación con el directorio de trabajo actual si el script está en o debajo del CWD
  • Adicionalmente, cwd(), getcwd()y abs_path()son proporcionados por el Cwdmódulo y se le indicará dónde la secuencia de comandos se ejecuta desde
  • El módulo FindBinproporciona las variables $Bin& $RealBinque generalmente son la ruta al script de ejecución; este módulo también proporciona $Scripty $RealScriptese es el nombre del script
  • __FILE__ es el archivo real que trata el intérprete de Perl durante la compilación, incluida su ruta completa.

He visto los primeros tres ( $0, el Cwdmódulo y el FindBinmódulo) fallar mod_perlespectacularmente, produciendo resultados sin valor como '.'o una cadena vacía. En tales entornos, uso __FILE__y obtengo la ruta a partir de eso usando el File::Basenamemódulo:

use File::Basename;
my $dirname = dirname(__FILE__);
Drew Stephens
fuente
2
Esta es realmente la mejor solución, especialmente si ya tiene $ 0 modificado
Caterham
8
Parece que abs_path debe usarse con _____FILE_____ ya que puede contener el nombre solo con la ruta.
Réplica
66
@vicTROLLA ¿Probablemente porque la mayor recomendación de esta respuesta (usando dirname con __FILE__) no funciona como se esperaba? Termino con la ruta relativa desde donde se ejecutó el script, mientras que la respuesta aceptada me da la ruta absoluta completa.
Izkata
10
dirname(__FILE__)no sigue los enlaces simbólicos, por lo que si vinculó el archivo ejecutable y esperaba encontrar la ubicación de algún otro archivo en la ubicación de instalación, debe verificarlo if( -l __FILE__)y luego dirname(readlink(__FILE__)).
DavidG
3
@IliaRostovtsev puede encontrar cuando un módulo se incluyó por primera vez en los módulos estándar con este encantamiento: perl -e 'use Module::CoreList; print Module::CoreList->first_release("File::Basename");'; echo. Para File::Basenameeso fue Perl 5.0.0, que se lanzó a finales de los 90, creo que ahora es seguro usarlo.
Drew Stephens el
145

$ 0 suele ser el nombre de su programa, entonces, ¿qué tal esto?

use Cwd 'abs_path';
print abs_path($0);

Me parece que esto debería funcionar como abs_path sabe si está utilizando una ruta relativa o absoluta.

Actualización Para cualquiera que lea esto años después, debería leer la respuesta de Drew . Es mucho mejor que el mío.

Ovidio
fuente
11
Pequeño comentario, en activestate perl en windows $ 0 típicamente contiene barras diagonales invertidas y abs_path devolvió barras diagonales hacia adelante, por lo que un rápido "tr / \ // \\ /;" fue necesario para arreglarlo.
Chris Madden
3
quería agregar que hay un realpath, que es sinónimo de abs_path, en caso de que prefiera el nombre sin guión bajo
vol7ron
@ Chris, ¿reportaste un error al mantenedor del módulo Cwd? Parece un error de adopción de Windows.
Znik
1
Otro problema que tengo: perl -e 'use Cwd "abs_path";print abs_path($0);' impresiones/tmp/-e
leonbloy
2
@leonbloy Cuando ejecuta un script en línea (con -e), creo que Perl crea un archivo temporal para almacenar su script en línea. Parece que la ubicación, en su caso, es /tmp. ¿Cuál esperaba que fuera el resultado?
GreenGiant
16

Creo que el módulo que estás buscando es FindBin:

#!/usr/bin/perl
use FindBin;

$0 = "stealth";
print "The actual path to this is: $FindBin::Bin/$FindBin::Script\n";
bmdhacks
fuente
11

Puede usar FindBin , Cwd , File :: Basename o una combinación de ellos. Todos están en la distribución base de Perl IIRC.

Usé Cwd en el pasado:

Cwd:

use Cwd qw(abs_path);
my $path = abs_path($0);
print "$path\n";
Benjamin W. Smith
fuente
@bmdhacks, tienes razón. La presunción es que no cambiaste 0 $. Por ejemplo, usted trabaja arriba tan pronto como se inicia el script (en el bloque de inicialización) o en otro lugar cuando no cambia $ 0. Pero $ 0 es una excelente manera de cambiar la descripción del proceso visible en la herramienta 'ps' unix :) Esto puede mostrar el estado actual del proceso, etc. Esto depende del propósito del programador :)
Znik
9

Obtener el camino absoluto hacia $0o __FILE__es lo que quieres. El único problema es que si alguien hizo un chdir()y $0fue relativo, entonces necesita obtener el camino absoluto en unBEGIN{} para evitar sorpresas.

FindBinintenta ir uno mejor y arrastrarse $PATHpor algo que coincida con elbasename($0) , pero hay momentos en que eso hace cosas demasiado sorprendentes (específicamente: cuando el archivo está "justo frente a ti" en el cwd).

File::Futiene File::Fu->program_namey File::Fu->program_dirpara esto.

Eric Wilhelm
fuente
¿Es realmente probable que alguien sea tan tonto como (permanentemente) chdir()en tiempo de compilación?
SamB
Simplemente haga todos los trabajos basados ​​en el directorio actual y $ 0 al comienzo del script.
Znik
7

Algunos antecedentes breves:

Desafortunadamente, la API de Unix no proporciona un programa en ejecución con la ruta completa al ejecutable. De hecho, el programa que ejecuta el suyo puede proporcionar lo que quiera en el campo que normalmente le dice a su programa qué es. Hay, como señalan todas las respuestas, varias heurísticas para encontrar posibles candidatos. Pero nada menos que buscar en todo el sistema de archivos siempre funcionará, e incluso eso fallará si el ejecutable se mueve o se elimina.

Pero no desea el ejecutable de Perl, que es lo que realmente se está ejecutando, sino el script que está ejecutando. Y Perl necesita saber dónde está el guión para encontrarlo. Almacena esto __FILE__, mientras que $0es de la API de Unix. Esto todavía puede ser un camino relativo, así que tome la sugerencia de Mark y canonícela conFile::Spec->rel2abs( __FILE__ );

wnoise
fuente
__FILE__Todavía me da un camino relativo. es decir '.'
Felwithe
6

Has probado:

$ENV{'SCRIPT_NAME'}

o

use FindBin '$Bin';
print "The script is located in $Bin.\n";

Realmente depende de cómo se llame y si es CGI o si se ejecuta desde un shell normal, etc.

Sean
fuente
$ ENV {'SCRIPT_NAME'} está vacío cuando el script se está ejecutando en la consola
Putnik
Mala idea porque el entorno SCRIPT_NAME depende del shell que esté utilizando. Esto es completamente incompatible con cmd.exe de Windows e incompatible cuando llama a un script directamente desde otros binarios. No hay garantía de que esta garantía esté establecida. Las formas anteriores son mucho más utilizables.
Znik
6

Para obtener la ruta al directorio que contiene mi script, ya utilicé una combinación de respuestas.

#!/usr/bin/perl
use strict;
use warnings;
use File::Spec;
use File::Basename;

my $dir = dirname(File::Spec->rel2abs(__FILE__));
Mate
fuente
2

perlfaq8 responde una pregunta muy similar al usar la rel2abs()función on $0. Esa función se puede encontrar en File :: Spec.

Moritz
fuente
2

No es necesario usar módulos externos, con solo una línea puede tener el nombre del archivo y la ruta relativa. Si está utilizando módulos y necesita aplicar una ruta relativa al directorio del script, la ruta relativa es suficiente.

$0 =~ m/(.+)[\/\\](.+)$/;
print "full path: $1, file name: $2\n";
daniel souza
fuente
No proporciona la ruta completa correcta del script si lo ejecuta como "./myscript.pl", ya que solo mostraría "." en lugar. Pero todavía me gusta esta solución.
Keve
1
#!/usr/bin/perl -w
use strict;


my $path = $0;
$path =~ s/\.\///g;
if ($path =~ /\//){
  if ($path =~ /^\//){
    $path =~ /^((\/[^\/]+){1,}\/)[^\/]+$/;
    $path = $1;
    }
  else {
    $path =~ /^(([^\/]+\/){1,})[^\/]+$/;
    my $path_b = $1;
    my $path_a = `pwd`;
    chop($path_a);
    $path = $path_a."/".$path_b;
    }
  }
else{
  $path = `pwd`;
  chop($path);
  $path.="/";
  }
$path =~ s/\/\//\//g;



print "\n$path\n";

: DD

mkc
fuente
44
Por favor, no solo conteste el código. Por favor explique por qué esta es la respuesta correcta.
Lee Taylor
1

¿Estás buscando esto ?:

my $thisfile = $1 if $0 =~
/\\([^\\]*)$|\/([^\/]*)$/;

print "You are running $thisfile
now.\n";

La salida se verá así:

You are running MyFileName.pl now.

Funciona tanto en Windows como en Unix.

Yong Li
fuente
0
use strict ; use warnings ; use Cwd 'abs_path';
    sub ResolveMyProductBaseDir { 

        # Start - Resolve the ProductBaseDir
        #resolve the run dir where this scripts is placed
        my $ScriptAbsolutPath = abs_path($0) ; 
        #debug print "\$ScriptAbsolutPath is $ScriptAbsolutPath \n" ;
        $ScriptAbsolutPath =~ m/^(.*)(\\|\/)(.*)\.([a-z]*)/; 
        $RunDir = $1 ; 
        #debug print "\$1 is $1 \n" ;
        #change the \'s to /'s if we are on Windows
        $RunDir =~s/\\/\//gi ; 
        my @DirParts = split ('/' , $RunDir) ; 
        for (my $count=0; $count < 4; $count++) {   pop @DirParts ;     }
        my $ProductBaseDir = join ( '/' , @DirParts ) ; 
        # Stop - Resolve the ProductBaseDir
        #debug print "ResolveMyProductBaseDir $ProductBaseDir is $ProductBaseDir \n" ; 
        return $ProductBaseDir ; 
    } #eof sub 
Yordan Georgiev
fuente
Si bien una respuesta de solo fuente puede resolver la pregunta del usuario, no les ayuda a entender por qué funciona. Le has dado al usuario un pez, pero en su lugar debes enseñarle a pescar.
The Tin Man
0

El problema con __FILE__ es que imprimirá la ruta principal del módulo ".pm", no necesariamente la ruta del script ".cgi" o ".pl" que se está ejecutando. Supongo que depende de cuál sea tu objetivo.

Me parece que Cwdsolo necesita actualizarse para mod_perl. Aquí está mi sugerencia:

my $path;

use File::Basename;
my $file = basename($ENV{SCRIPT_NAME});

if (exists $ENV{MOD_PERL} && ($ENV{MOD_PERL_API_VERSION} < 2)) {
  if ($^O =~/Win/) {
    $path = `echo %cd%`;
    chop $path;
    $path =~ s!\\!/!g;
    $path .= $ENV{SCRIPT_NAME};
  }
  else {
    $path = `pwd`;
    $path .= "/$file";
  }
  # add support for other operating systems
}
else {
  require Cwd;
  $path = Cwd::getcwd()."/$file";
}
print $path;

Por favor agregue cualquier sugerencia.

Jonathan
fuente
0

Sin ningún módulo externo, válido para shell, funciona bien incluso con '../':

my $self = `pwd`;
chomp $self;
$self .='/'.$1 if $0 =~/([^\/]*)$/; #keep the filename only
print "self=$self\n";

prueba:

$ /my/temp/Host$ perl ./host-mod.pl 
self=/my/temp/Host/host-mod.pl

$ /my/temp/Host$ ./host-mod.pl 
self=/my/temp/Host/host-mod.pl

$ /my/temp/Host$ ../Host/./host-mod.pl 
self=/my/temp/Host/host-mod.pl
Putnik
fuente
¿Qué sucede cuando llamas al enlace simbólico? Cwd funciona excelente con este caso.
Znik
0

El problema con solo usar dirname(__FILE__)es que no sigue enlaces simbólicos. Tuve que usar esto para que mi script siguiera el enlace simbólico a la ubicación real del archivo.

use File::Basename;
my $script_dir = undef;
if(-l __FILE__) {
  $script_dir = dirname(readlink(__FILE__));
}
else {
  $script_dir = dirname(__FILE__);
}
DavidG
fuente
0

Todas las soluciones sin biblioteca en realidad no funcionan para más de unas pocas formas de escribir una ruta (piense ../ o /bla/x/../bin/./x/../ etc. Mi solución se parece a a continuación. Tengo una peculiaridad: no tengo la menor idea de por qué tengo que ejecutar los reemplazos dos veces. Si no lo hago, obtengo un "./" o "../" falso. Aparte de eso, me parece bastante robusto

  my $callpath = $0;
  my $pwd = `pwd`; chomp($pwd);

  # if called relative -> add pwd in front
  if ($callpath !~ /^\//) { $callpath = $pwd."/".$callpath; }  

  # do the cleanup
  $callpath =~ s!^\./!!;                          # starts with ./ -> drop
  $callpath =~ s!/\./!/!g;                        # /./ -> /
  $callpath =~ s!/\./!/!g;                        # /./ -> /        (twice)

  $callpath =~ s!/[^/]+/\.\./!/!g;                # /xxx/../ -> /
  $callpath =~ s!/[^/]+/\.\./!/!g;                # /xxx/../ -> /   (twice)

  my $calldir = $callpath;
  $calldir =~ s/(.*)\/([^\/]+)/$1/;
Elmar
fuente
0

Ninguna de las respuestas "principales" fue adecuada para mí. El problema con el uso de FindBin '$ Bin' o Cwd es que devuelven la ruta absoluta con todos los enlaces simbólicos resueltos. En mi caso, necesitaba la ruta exacta con enlaces simbólicos presentes, lo mismo que devuelve el comando Unix "pwd" y no "pwd -P". La siguiente función proporciona la solución:

sub get_script_full_path {
    use File::Basename;
    use File::Spec;
    use Cwd qw(chdir cwd);
    my $curr_dir = cwd();
    chdir(dirname($0));
    my $dir = $ENV{PWD};
    chdir( $curr_dir);
    return File::Spec->catfile($dir, basename($0));
}
drjumper
fuente
0

En Windows, usar dirnamey abs_pathjuntos funcionó mejor para mí.

use File::Basename;
use Cwd qw(abs_path);

# absolute path of the directory containing the executing script
my $abs_dirname = dirname(abs_path($0));
print "\ndirname(abs_path(\$0)) -> $abs_dirname\n";

este es el por qué:

# this gives the answer I want in relative path form, not absolute
my $rel_dirname = dirname(__FILE__); 
print "dirname(__FILE__) -> $rel_dirname\n"; 

# this gives the slightly wrong answer, but in the form I want 
my $full_filepath = abs_path($0);
print "abs_path(\$0) -> $full_filepath\n";
usuario3228609
fuente
-2

¿Qué tiene de malo $^X?

#!/usr/bin/env perl<br>
print "This is executed by $^X\n";

Le daría la ruta completa al binario de Perl que se está utilizando.

Evert

usuario3061015
fuente
1
Da camino al binario de Perl mientras que se requiere un guión
Putnik
-5

En * nix, es probable que tenga el comando "whereis", que busca en su $ PATH buscando un binario con un nombre de pila. Si $ 0 no contiene el nombre completo de la ruta, ejecutar whereis $ scriptname y guardar el resultado en una variable debería indicarle dónde se encuentra el script.

foxxtrot
fuente
Eso no funcionará, ya que $ 0 también podría devolver una ruta relativa al archivo: ../perl/test.pl
Lathan
¿Qué sucederá si el script ejecutable está fuera de PATH?
Znik