Agregue una nueva línea en un nombre de archivo con `mv`

11

Es una pregunta seria. Pruebo algunos awkscripts y necesito archivos con una nueva línea en sus nombres.


¿Es posible agregar una nueva línea en un nombre de archivo con mv?

Ahora, puedo hacer esto con touch:

touch "foo
bar"

Con el toque agregué el carácter de nueva línea por copiar y pegar. Pero no puedo escribir fooReturnbaren mi caparazón.

¿Cómo puedo cambiar el nombre de un archivo para tener una nueva línea en el nombre del archivo?


Editar 28/06/2015; 07:08 pm

Para agregar una nueva línea zsh, puedo usar, Alt+Return

AB
fuente
2
¿Por qué preguntas? ¿Estás haciendo una broma o un truco a un amigo?
Basile Starynkevitch
2
No elimines la pregunta, es útil. Pero realmente evite las nuevas líneas en los nombres de archivo tanto como sea posible.
Basile Starynkevitch
2
¿Qué te hace pensar que hacerlo mvsería diferente de hacerlo touch? ¿Intentaste lo mismo?
Kevin
2
Bueno, puedes copiar y pegar en el mvcomando tan fácilmente como touch.
Kevin
2
Entonces, permítame sugerirle que formule más claramente su OP: Dentro de un entorno de shell dado, no puedo escribir una nueva línea dentro de una cadena entre comillas como en echo "a [return] b".
dan

Respuestas:

22

Es una mala idea (tener caracteres extraños en los nombres de los archivos) pero podría hacerlo

 mv somefile.txt "foo
 bar"

(también se podría haber hecho mv somefile.txt "$(printf "foo\nbar")"o mv somefile.txt foo$'\n'bar, etc ... detalles son específicos a su concha. Estoy usando zsh)

Lea más sobre globbing , por ejemplo, glob (7) . Los detalles pueden ser específicos del shell. Pero comprenda que /bin/mv está dado (por su shell), a través de execve (2) , una matriz expandida de argumentos: la expansión de argumentos y el globbing es responsabilidad del shell que invoca.

E incluso podría codificar un pequeño programa en C para hacer lo mismo:

#include <stdio.h>
#include <stdlib.h>
int main() {
  if (rename ("somefile.txt", "foo\nbar")) {
     perror("rename somefile.txt");
     exit(EXIT_FAILURE);
  };
  return 0;
}

Guarde el programa anterior en foo.c, compílelo y gcc -Wall foo.c -o foo luego ejecútelo./foo

Del mismo modo, puede codificar un script similar en Perl, Ruby, Python, Ocaml, etc.

Pero esa es una mala idea. Evite las nuevas líneas en los nombres de archivo (confundirá al usuario y podría romper muchos scripts).

En realidad, incluso recomiendo usar solo letras, dígitos y +-/._% caracteres no acentuados ( / siendo el separador de directorio) en las rutas de archivo. Los archivos "ocultos" (que comienzan con .) deben usarse con precaución y parcimony. Creo que usar cualquier tipo de espacio en un nombre de archivo es un error. Utilice un guión bajo en su lugar (p foo/bar_bee1.txt. Ej. ) O un signo menos (p foo/bar-bee1.txt. Ej. )

Basile Starynkevitch
fuente
Sí, lo sé, puedo copiar y pegar un carácter de nueva línea, pero ¿cómo podría hacerlo sin copiar y pegar?
AB
44
No hay ninguna copia o pega aquí. Hay una nueva línea escrita después foo.
dan
Sin embargo, también funciona copiando y pegando
kos
1
Es una mala idea para los archivos habituales, pero es un buen caso de prueba, como mencionó el OP.
artdanil
55
@y ,sería más inofensivo que -(como en la primera posición) o %. Todas esas recomendaciones son principalmente para evitar errores en algunas aplicaciones (que rompen los nombres de archivo que de otra manera serían válidos desde el punto de vista del sistema operativo). Se siente al revés pedirle a la gente que no use esos personajes en lugar de arreglar sus errores.
Stéphane Chazelas
19

Si usa bash, este comando debería funcionar.

mv a $'b\nc'
favadi
fuente
44
Funciona zshtambién con =)
AB
3
bash, como zsh y FreeBSD sh lo copiaron de ksh93.
Stéphane Chazelas
¡Muchas gracias! Me había olvidado por completo alrededor de $ en bash, mientras que era mi propia respuesta en SO asesorar a usarlo - stackoverflow.com/questions/1825552/grep-a-tab-in-unix/...
antimirov
6

Sé que solicitó una mvsolución, sin embargo, a pesar de la advertencia, esto se puede hacer fácilmente con rename(en el paquete Perl):

~/tmp$ touch foo
~/tmp$ rename 's/$/\nbar/' foo
Unsuccessful stat on filename containing newline at /usr/share/perl5/File/Rename.pm line 69.
~/tmp$ ls
foo?bar
kos
fuente
Gracias por la rename, una muy buena idea. +1
AB
5

Un aspecto de este problema no se trata realmente awk, y solo un poco sobre el shell. El problema es que, en un tty canónico estándar, la mayoría de las veces la disciplina de tty del kernel está amortiguando su entrada, simplemente haciéndola eco en su pantalla y en ningún otro lugar, para que pueda manejar eficientemente el retroceso y cosas por el estilo.

Sin embargo, cuando presiona Intro o ingresa una nueva línea, todos esos datos almacenados en el búfer se envían a la vez a la aplicación de lectura, generalmente su shell. Puede observar esto observando $PS2después de ingresar una cita pendiente. Cuando el shell se imprime $PS2es porque solo lee un bloque de su entrada y aún no está convencido de que haya terminado.

Por lo tanto, para mayor comodidad, lo que necesita es alguna forma de enviar una línea \nelectrónica al búfer de terminal sin tener que presionar toda esa otra entrada de inmediato. La forma estándar de hacerlo es con la secuencia de teclas CTRL+V, que cita para el terminal su próximo carácter de entrada. Hacer CTRL+Va continuación, CTRL+J- porque esta última es por lo general cómo escribir un literal \newline. Sabrás que funciona cuando no ves $PS2porque el shell todavía no ha leído tu entrada.

Nota sin embargo que cuando no leyó que la entrada de su anterior CTRL+Vhabrá hecho ninguna diferencia para la cáscara del todo - que sólo lo cita para la línea-disciplina. Definitivamente querrás citar la nueva línea para hacer algo significativo con ella.

Por cierto, CTRL+Vse puede aplicar útilmente de otras maneras, por ejemplo, "$(printf \\33)"no es la única forma de escribir un ESCpersonaje en un script de shell, y ni siquiera es la más simple. Literalmente, puede ingresar cualquier carácter que su teclado enviará sin que el controlador de entrada intente interpretarlo si primero lo escapa de esta manera.

A menudo me gusta usar <tab> s en la línea de comandos sin que el shell intente completar nada. Debido a que los shells que completan generalmente configurarán <tab> de una manera que stty eol \t, para que sus sistemas de finalización funcionen, CTRL+Vme funciona incluso en entornos desconocidos.

mikeserv
fuente
2
Gracias por mencionar 'Ctrl + V'. Estaba a punto de publicar una respuesta al respecto y luego vi su explicación.
artdanil