Estoy tratando de usar Perl para reordenar el contenido de un archivo md5. Para cada línea, quiero el nombre de archivo sin la ruta y luego el hash. El mejor comando que se me ocurrió es:

$ perl -pe 's|^([[:alnum:]]+).*?([^/]+)$|$2 $1|' DCIM.md5

El archivo de entrada (DCIM.md5) es producido por md5sum en Linux. Se parece a esto:

e26ff03dc1bac80226e200c0c63d17a2  ./Path1/IMG_20150201_160548.jpg
01f92572e4c6f2ea42bd904497e4f939  ./Path 2/IMG_20150204_190528.jpg
afce027c977944188b4f97c5dd1bd101  ./Path3/Path 4/IMG_20151011_193008.jpg
  1. El hash coincide con el primer grupo ([[:alnum:]]+) en
    expresión regular.
  2. Entonces los espacios y la ruta al archivo son
    coincide con .*?.
  3. Entonces el nombre de archivo se corresponde con ([^/]+).
  4. La expresión está encerrada con ^ (aparentemente no necesario aquí) y $. Sin $, la expresión no genera lo que esperaba.
  5. Yo uso | en lugar de / como separador para evitar escapar en las rutas de archivo.

Ese comando devuelve:

IMG_20150201_160548.jpg
 e26ff03dc1bac80226e200c0c63d17a2IMG_20150204_190528.jpg
 01f92572e4c6f2ea42bd904497e4f939IMG_20151011_193008.jpg
 afce027c977944188b4f97c5dd1bd101IMG_20151011_195133.jpg

La coincidencia es correcta, la secuencia de salida es correcta (nombre de archivo sin ruta y luego hash) pero el espaciado no lo es: hay una nueva línea después del nombre de archivo. Lo espero después del hash, así:

IMG_20150201_160548.jpg e26ff03dc1bac80226e200c0c63d17a2
IMG_20150204_190528.jpg 01f92572e4c6f2ea42bd904497e4f939
IMG_20151011_193008.jpg afce027c977944188b4f97c5dd1bd101

Me parece que mi comando genera el carácter de nueva línea, pero no sé cómo cambiar este comportamiento. ¿O posiblemente el problema proviene del shell, no del comando?

Finalmente, alguna información de versión:

$ perl -version
This is perl 5, version 22, subversion 1 (v5.22.1) built for i686-linux-gnu-thread-multi-64int
(with 69 registered patches, see perl -V for more detail)
2
raph82 15 sep. 2018 a las 20:47

4 respuestas

La mejor respuesta

[^/]+ coincide con las nuevas líneas, por lo que las que están en su entrada son parte de $2, que se coloca primero en su $_ transformado (y no hay nueva línea en $1, por lo que no hay nueva línea al final de $_ ...)

Solución: lea la opción -l de perlrun. En particular:

-l [octnum] habilita el procesamiento automático de final de línea. Tiene dos efectos separados. Primero, automáticamente divide $ / (el separador de registro de entrada) cuando se usa con -n o -p. En segundo lugar, asigna $ \ (el separador de registro de salida) para tener el valor de octnum, de modo que cualquier declaración de impresión tendrá ese separador agregado nuevamente. Si se omite octnum, establece $ \ en el valor actual de $ /.

5
Shawn 15 sep. 2018 a las 17:57

Hacer una sustitución te deja tener que escribir un patrón de expresiones regulares que coincida con todo lo que no deseas y con todo lo que haces. Por lo general, es mucho mejor combinar solo las partes que necesita y construir otra cadena a partir de ellas

Así

for ( <> ) {
    die unless m< (\w++) .*? ([^/\s]+) \s* \z >x;
    print "$2 $1\n";
}

O si debe tener una sola línea

perl -ne 'die unless m< (\w++) .*? ([^/\s]+) \s*\z >x; print "$2 $1\n";' myfile.md5

Salida

IMG_20150201_160548.jpg e26ff03dc1bac80226e200c0c63d17a2
IMG_20150204_190528.jpg 01f92572e4c6f2ea42bd904497e4f939
IMG_20151011_193008.jpg afce027c977944188b4f97c5dd1bd101
2
Borodin 15 sep. 2018 a las 18:16

Use [^/\n] en lugar de [^/]:

perl -pe 's|^([[:alnum:]]+).*?([^/\n]+)$|$2 $1|' DCIM.md5

2
CrafterKolyan 15 sep. 2018 a las 17:58

Solución alternativa, que utiliza muchos conceptos de otras respuestas y comentarios ...

$ perl -pe 's|(\p{hex}+).*?([^/]+?)$|$2 $1|' DCIM.md5

... y explicación.

Después de investigar todas las respuestas e intentar resolverlas, decidí que la base del problema es que [^/]+ es codicioso . Su avaricia hace que capture la nueva línea; ignora el ancla $.

Esto fue difícil de entender para mí, ya que analicé mucho usando sed antes de usar Perl, e incluso un comodín codicioso no capturará una nueva línea en sed. Espero que esta publicación ayude a aquellos que (acostumbrados a sed como yo) también se preguntan (como lo hice) por qué $ no está actuando "como esperaba".

Podemos ver el problema "codicioso" al intentar lo que publicaré como otra respuesta alternativa.

Escribe el archivo:

$ cat > DCIM.md5<<EOF
> e26ff03dc1bac80226e200c0c63d17a2  ./Path1/IMG_20150201_160548.jpg
> 01f92572e4c6f2ea42bd904497e4f939  ./Path 2/IMG_20150204_190528.jpg
> afce027c977944188b4f97c5dd1bd101  ./Path3/Path 4/IMG_20151011_193008.jpg
> EOF

Deshágase del codicioso [^/]+ cambiándolo a [^/]+?. Analizar gramaticalmente.

$ perl -pe 's|([[:alnum:]]+).*?([^/]+?)$|$2 $1|' DCIM.md5
IMG_20150201_160548.jpg e26ff03dc1bac80226e200c0c63d17a2
IMG_20150204_190528.jpg 01f92572e4c6f2ea42bd904497e4f939
IMG_20151011_193008.jpg afce027c977944188b4f97c5dd1bd101

Salida deseada lograda.

La respuesta aceptada, de @Shawn,

$ perl -lpe 's|^([[:alnum:]]+).*?([^/]+)$|$2 $1|' DCIM.md5

Básicamente cambia el ancla $ para que se comporte de la manera en que una persona sed lo esperaría.

La respuesta de @CrafterKolyan se encarga de la codiciosa [^/] que captura la nueva línea diciendo que no puede tener un barra diagonal o una nueva línea. Esta respuesta aún necesita el ancla $ para evitar la siguiente situación

1) .* captura la cadena vacía ( 0 o más de cualquier carácter)

2) [^/\n]+ captura ..

La respuesta de @Borodin adopta un enfoque bastante diferente, pero es un gran concepto.

@Borodin, además, hizo un excelente comentario que permite una versión más precisa / más exacta de esta respuesta, que es la versión que puse en la parte superior de esta publicación.

Finalmente, si uno quiere seguir el modelo de programación Perl, aquí hay otra alternativa.

$ perl -pe 's|([[:xdigit:]]+).*?([^/]+?)(\n\|\Z)|$2 $1$3|' DCIM.md5

PD Porque sed no es como perl (sin comodines no codiciosos,) aquí hay un sed ejemplo que muestra el comportamiento que discuto.

$ sed 's|^\([[:alnum:]]\+\).*/\([^/]\+\)$|\2 \1|' DCIM.md5

Esto es básicamente una "traducción directa" de la expresión perl excepto para las '/' adicionales antes de las [^/]. Espero que ayude a aquellos que comparan sed y perl.

3
bballdave025 15 sep. 2018 a las 22:58