Cambiar nombre devuelve "no se permite la palabra básica" cuando se intenta minúsculas partes de múltiples nombres de archivo

12

Tengo dos archivos en una carpeta en mi Ubuntu 16.04:

a1.dat
b1.DAT

Quiero cambiar el nombre b1.DATa b1.datpara tener los siguientes archivos como resultado en la carpeta:

a1.dat
b1.dat

Intenté (sin éxito):

$ rename *.DAT *.dat
Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

y

$ find . -iname "*.DAT" -exec rename DAT dat '{}' \;
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

Buscar esto no resultó en una solución significativa ...

Samo
fuente
También es posible que desee aprender sobre mmv
PlasmaHH

Respuestas:

14

Parece que ese error proviene de Perl rename. Necesita usar comillas, pero solo necesita especificar la parte que desea cambiar, buscar y reemplazar estilo. La sintaxis es así:

rename -n 's/\.DAT/\.dat/' *

Eliminar -ndespués de la prueba para cambiar el nombre de los archivos.

Para incluir archivos ocultos, cambie la configuración global antes de ejecutar el comando:

shopt -s dotglob

Si desea cambiar el nombre de los archivos de forma recursiva, puede usar

shopt -s globstar
rename -n 's/\.DAT/\.dat/' **

o si hay muchas rutas en o debajo del directorio actual que no terminan .DAT, será mejor que especifique esas rutas en el segundo comando:

rename -n 's/\.DAT/\.dat/' **/*.DAT

Esto será más rápido si sus archivos tienen varios nombres diferentes que no terminan en .DAT. [1]

Para desactivar estas configuraciones, puede usar shopt -u, por ejemplo shopt -u globstar, pero están desactivadas de manera predeterminada y se desactivarán cuando abra un nuevo shell.

Si esto resulta en una lista de argumentos excesivamente larga, puede usar, por ejemplo find:

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} \;

o mejor

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} +

Usar find ... -execcon +es más rápido que usar \;porque construye una lista de argumentos a partir de los archivos encontrados. Originalmente pensé que no sería posible usarlo, porque mencionó que tenía un argument list too longproblema, pero ahora sé que la lista también se dividirá hábilmente en múltiples invocaciones del comando según sea necesario para evitar ese problema. [2] .

Dado renameque procesará cada nombre de archivo de la misma manera, no importa qué tan larga sea la lista de argumentos, ya que se puede dividir de forma segura en varias invocaciones. Si el comando con el que está utilizando -execno acepta múltiples argumentos o requiere que sus argumentos estén en un orden particular o por cualquier otra razón, dividir la lista de argumentos hará que suceda algo no deseado, puede usarlo \;, lo que hace que el comando se invoque una vez para cada archivo encontrado (si la lista de argumentos era demasiado larga para otros métodos, ¡esto llevará mucho tiempo!).


Muchas gracias a Eliah Kagan por las sugerencias muy útiles para mejorar esta respuesta:

[1] Especificando los nombres de archivo al hacer globbing .
[2] find ... -execcon +divide la lista de argumentos .

Zanna
fuente
Gracias. ¿Cómo hacerlo recursivamente (para todos los archivos en todas las subcarpetas)?
Samo
@Samo mira mi edición :)
Zanna
No funciona: $ rename 's / \. DAT / \. Dat /' ** bash: / usr / bin / rename: Lista de argumentos demasiado larga
Samo
1
@Samo, debe eliminar el -ncambio de nombre después de la prueba, es solo para mostrar lo que se cambiará
Zanna
1
En Centos y Arch, renombrar no exhibe este comportamiento. Llegué aquí usando Debian en WSL. Esta sintaxis funcionó.
xtian
6

Tu puedes hacer:

rename -n 's/DAT$/\L$&/' *.DAT

Descarte -npara que tenga lugar el cambio de nombre real.

  • el patrón global *.DATcoincide con todos los archivos que terminan .DATen el directorio actual

  • en la renamesustitución de s, DAT$partidos DATal final

  • \L$&hace todo el partido en minúsculas; $&se refiere a todo el partido

Si solo quieres hacer por b1.DAT:

rename -n 's/DAT$/\L$&/' b1.DAT

Ejemplo:

% rename -n 's/DAT$/\L$&/' *.DAT
rename(b1.DAT, b1.dat)
heemayl
fuente
4

Otras respuestas han abordado uno de los dos aspectos principales de esta pregunta: el de cómo llevar a cabo con éxito la operación de cambio de nombre que necesitaba. El propósito de esta respuesta es explicar por qué sus comandos no funcionaron, incluido el significado de ese extraño mensaje de error "no se permite la palabra básica" en el contexto del renamecomando.

La primera sección de esta respuesta trata sobre la relación entre renamePerl y cómo renameutiliza el primer argumento de línea de comando que le pasa, que es su argumento de código. La segunda sección trata sobre cómo el shell realiza expansiones, específicamente globbing, para construir una lista de argumentos. La tercera sección trata sobre lo que está sucediendo en el código Perl que proporciona errores de "Bareword no permitido". Finalmente, la cuarta sección es un resumen de todos los pasos que se realizan entre ingresar el comando y obtener el error.

1. Cuando renamereciba mensajes de error extraños, agregue "Perl" a su búsqueda.

En Debian y Ubuntu, el renamecomando es un script de Perl que realiza el cambio de nombre de archivo. En versiones anteriores, incluido 14.04 LTS, que todavía se admite a partir de este escrito, era un enlace simbólico que apuntaba ( indirectamente ) al prenamecomando. En las versiones más recientes, apunta al file-renamecomando más nuevo . Esos dos comandos de cambio de nombre de Perl funcionan principalmente de la misma manera, y solo me referiré a ambos como renameal resto de esta respuesta.

Cuando usa el renamecomando, no solo está ejecutando el código Perl que otra persona ha escrito. También está escribiendo su propio código Perl y le dice renameque lo ejecute. Esto se debe a que el primer argumento de la línea de comandos que pasa al renamecomando, que no sean argumentos de opciones como -n, consiste en un código Perl real . El renamecomando usa este código para operar en cada uno de los nombres de ruta que le pasa como argumentos de línea de comando posteriores. (Si no pasa ningún argumento de nombre de ruta, en su lugar renamelee los nombres de ruta de la entrada estándar , uno por línea).

El código se ejecuta dentro de un bucle , que itera una vez por nombre de ruta. En la parte superior de cada iteración del bucle, antes de que se ejecute su código, a la $_variable especial se le asigna el nombre de ruta que se está procesando actualmente. Si su código hace que el valor de $_se cambie a otra cosa, entonces se cambia el nombre de ese archivo para que tenga el nuevo nombre.

Muchas expresiones en Perl operan implícitamente en la $_variable cuando no se les da ninguna otra expresión para usar como operando . Por ejemplo, la expresión de sustitución $str =~ s/foo/bar/cambia la primera aparición de fooen la cadena mantenida por la $str variable a bar, o la deja sin cambios si no contiene foo. Si solo escribe s/foo/bar/sin usar explícitamente el =~operador , entonces funciona $_. Esto quiere decir que s/foo/bar/es la abreviatura de $_ =~ s/foo/bar/.

Es común para pasar una s///expresión de renameque el argumento de código (es decir, el primer argumento de línea de comandos), pero no tiene que hacerlo. Puede darle cualquier código Perl que desee que se ejecute dentro del bucle, para examinar cada valor $_y (condicionalmente) modificarlo.

Esto tiene muchas consecuencias interesantes y útiles , pero la gran mayoría de ellas están más allá del alcance de esta pregunta y respuesta. La razón principal por la que traigo esto aquí, de hecho, la razón principal por la que decidí publicar esta respuesta, es para señalar que, debido a que el primer argumento renamees en realidad el código Perl, cada vez que recibe un mensaje de error extraño y usted Si tiene problemas para encontrar información al buscar, puede agregar "Perl" a la cadena de búsqueda (o incluso reemplazar "cambiar el nombre" por "Perl", a veces) y con frecuencia encontrará una respuesta.

2. Con rename *.DAT *.dat, el renamecomando nunca vio *.DAT!

Un comando como rename s/foo/bar/ *.txtno suele pasar *.txtcomo un argumento de línea de comando al renameprograma, y usted no quiere que lo haga, a menos que tenga un archivo cuyo nombre sea literalmente *.txt, que con suerte no lo hará.

renameno interpreta patrones globales como *.txt, *.DAT, *.dat, x*, *y, o *cuando se le pasa como argumento de nombre de ruta. En cambio, su shell realiza la expansión del nombre de ruta en ellos (que también se llama expansión de nombre de archivo, y también llamada globalización). Esto sucede antes de que renamese ejecute la utilidad. El shell expande los globos en nombres de ruta potencialmente múltiples y los pasa a todos, como argumentos de línea de comandos separados rename. En Ubuntu, su shell interactivo es Bash , a menos que lo haya cambiado, razón por la cual me he vinculado al manual de referencia de Bash anterior.

Hay una situación en la que se puede pasar un patrón global como un único argumento de línea de comandos sin expandir a rename: cuando no coincide con ningún archivo. Diferentes shells exhiben un comportamiento predeterminado diferente en esta situación, pero el comportamiento predeterminado de Bash es simplemente pasar el glob literalmente. Sin embargo, ¡raramente quieres esto! Si lo deseaba, debe asegurarse de que el patrón no se expande, citandolo . Esto sirve para pasar argumentos a cualquier comando, no solo a rename.

Las citas no son solo para globbing (expansión de nombre de archivo), porque hay otras expansiones que su shell realiza en texto sin comillas y, para algunas de ellas pero no para otras , también en texto entre " "comillas. En general, cada vez que desee pasar un argumento que contenga caracteres que pueden ser tratados especialmente por el shell, incluidos los espacios, debe citarlo, preferiblemente con ' 'comillas .

El código Perl s/foo/bar/no contiene nada tratado especialmente por el shell, pero habría sido una buena idea para mí haberlo citado también, y escrito 's/foo/bar/'. (De hecho, la única razón por la que no era que iba a ser confuso para algunos lectores, ya que todavía no había hablado de citar.) La razón por la que digo esto habría sido bueno es porque es muy común que el código Perl no contiene tales caracteres, y si tuviera que cambiar ese código, podría no recordar comprobar si era necesario citar. Por el contrario, si desea que la carcasa se expanda un pegote, debe no ser citado.

3. Lo que el intérprete de Perl quiere decir con "palabra no permitida"

Los mensajes de error que mostró en su pregunta revelan que, cuando ejecutó rename *.DAT *.dat, su shell se expandió *.DATen una lista de uno o más nombres de archivo, y que el primero de esos nombres era b1.DAT. Todos los argumentos subsiguientes, tanto los demás expandidos *.DATcomo los que se expandieron desde, *.datsurgieron después de ese argumento, por lo que habrían sido interpretados como nombres de ruta.

Debido a que lo que realmente funcionó fue algo así rename b1.DAT ..., y debido a que renametrata su primer argumento sin opción como código Perl, la pregunta es: ¿por qué b1.DATproduce estos errores de "no se permite la palabra pura" cuando lo ejecuta como código Perl?

Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).

En un shell, citamos nuestras cadenas para protegerlas de expansiones de shell no intencionadas que de otro modo las transformarían en otras cadenas automáticamente (consulte la sección anterior). Los shells son lenguajes de programación de propósito especial que funcionan de manera muy diferente a los lenguajes de propósito general (y su sintaxis y semántica muy extrañas lo reflejan). Pero Perl es un lenguaje de programación de propósito general y, como la mayoría de los lenguajes de programación de propósito general, el propósito principal de las citas en Perl no es proteger las cadenas, sino mencionarlas. Esta es en realidad una forma en que la mayoría de los lenguajes de programación son similares al lenguaje natural. En inglés, y suponiendo que tenga un perro, "su perro" es una frase de dos palabras, mientras que su perro es un perro. Del mismo modo, en Perl, '$foo'es una cadena, mientras que $fooes algo cuyo nombre es $foo.

Sin embargo, a diferencia de casi cualquier otro lenguaje de programación de propósito general, Perl también a veces interpretar el texto no indicada como mencionar una cadena - una cadena que es la "misma", ya que, en el sentido de que está compuesto por los mismos caracteres en El mismo orden. Solo intentará interpretar el código de esa manera si es una palabra simple (sin $u otro sigilo, ver más abajo), y después no pudo encontrar ningún otro significado para darle. Luego lo tomará como una cadena, a menos que le diga que no lo haga habilitando restricciones .

Las variables en Perl generalmente comienzan con un carácter de puntuación llamado sigilo , que especifica el tipo amplio de la variable. Por ejemplo, $significa escalar , @significa matriz y %significa hash . ( Hay otros ) . No se preocupe si lo encuentra confuso (o aburrido), porque solo lo menciono para decir que cuando aparece un nombre válido en un programa Perl pero no está precedido por un sigilo, ese nombre se dice que es una palabra desnuda .

Las palabras desnudas sirven para varios propósitos, pero generalmente significan una función incorporada o una subrutina definida por el usuario que se ha definido en el programa (o en un módulo utilizado por el programa). Perl no se ha incorporado en las funciones llamadas b1o DAT, de modo que cuando el intérprete Perl ve el código b1.DAT, se trata de tratar b1y DATcomo los nombres de subrutinas. Suponiendo que no se hayan definido tales subrutinas, esto falla. Luego, siempre que no se hayan habilitado las restricciones , las trata como cadenas. Esto funcionará, aunque nadie sabe si realmente pretendía que esto sucediera o no . El .operador de Perl concatena cadenas , por lo que b1.DATevalúa la cadenab1DAT. Es decir, b1.DATes una mala forma de escribir algo como 'b1' . 'DAT'o "b1" . "DAT".

Puede probar esto usted mismo ejecutando el comando perl -E 'say b1.DAT', que pasa el breve script say b1.DATPerl al intérprete Perl, que lo ejecuta, imprimiendo b1DAT. (En ese orden, las ' 'cotizaciones cuentan la cáscara de pasar say b1.DATcomo un solo argumento de línea de comandos, de lo contrario, el espacio podría causar sayy b1.DATque se analiza como palabras separadas y perllos recibiría como argumentos separados. perlNo no ver las cotizaciones a sí mismos, como el la cáscara los elimina )

Pero ahora, intente escribiruse strict; en el script de Perl antes say. Ahora falla con el mismo tipo de error que obtuvo de rename:

$ perl -E 'use strict; say b1.DAT'
Bareword "b1" not allowed while "strict subs" in use at -e line 1.
Bareword "DAT" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.

Esto sucedió porque use strict;prohibió al intérprete de Perl tratar las palabras desnudas como cadenas. Para prohibir esta característica particular, realmente sería suficiente habilitar solo la subsrestricción. Este comando produce los mismos errores que arriba:

perl -E 'use strict "subs"; say b1.DAT'

Pero generalmente los programadores de Perl simplemente escriben use strict;, lo que habilita la subsrestricción y otras dos. use strict;Generalmente se recomienda la práctica. Entonces el renamecomando hace esto para su código. Es por eso que recibe ese mensaje de error.

4. En resumen, esto es lo que sucedió:

  1. Su shell pasó b1.DATcomo el primer argumento de línea de comandos, que se renametrató como un código Perl para ejecutarse en un bucle para cada argumento de nombre de ruta.
  2. Esto fue tomado en el sentido b1y DATconectado con el .operador.
  3. b1y DATno tenían prefijos con sigilos, por lo que fueron tratados como palabras desnudas.
  4. Esas dos palabras simples se habrían tomado como nombres de funciones integradas o cualquier subrutina definida por el usuario, pero nada de eso tenía ninguno de esos nombres.
  5. Si los "subs estrictos" no hubieran sido habilitados, entonces habrían sido tratados como las expresiones de cadena 'b1'y 'DAT'y concatenados. Eso está lejos de lo que pretendía, lo que ilustra cómo esta característica a menudo no es útil.
  6. Pero "estrictas" submarinos se habilitó, porque renamepermite a todos restricciones ( vars, refsy subs). Por lo tanto, recibió un error en su lugar.
  7. renameSalir debido a este error. Debido a que este tipo de error ocurrió antes, no se realizaron intentos de cambio de nombre de archivo, aunque no lo haya aprobado -n. Esto es algo bueno, que a menudo protege a los usuarios de cambios involuntarios de nombre de archivo y, en ocasiones, incluso de la pérdida real de datos.

Gracias a Zanna , quien me ayudó a abordar varias deficiencias importantes en un borrador anterior de esta respuesta . Sin ella, esta respuesta habría tenido menos sentido y podría no publicarse en absoluto.

Eliah Kagan
fuente