Adicione uma nova linha a um nome de arquivo com `mv`

11

É uma pergunta séria. Testo alguns awkscripts e preciso de arquivos com uma nova linha em seus nomes.


É possível adicionar uma nova linha em um nome de arquivo mv?

Agora eu posso fazer isso com touch:

touch "foo
bar"

Com o toque, adicionei o caractere de nova linha por copiar e colar. Mas não consigo escrever fooReturnbarna minha concha.

Como posso renomear um arquivo para ter uma nova linha no nome do arquivo?


Editar 28/06/2015; 19:08

Para adicionar uma nova linha em zshque eu posso usar, Alt+Return

AB
fonte
2
Por que você pergunta? Você está fazendo alguma piada ou truque para um amigo?
Basile Starynkevitch
2
Não remova a pergunta, é útil. Mas evite novas linhas nos nomes de arquivos o máximo possível.
Basile Starynkevitch
2
O que faz você pensar que fazê-lo mvseria diferente de fazê-lo touch? Você tentou a mesma coisa?
28415 Kevin
2
Bem, você pode copiar e colar no mvcomando com a mesma facilidade touch.
Kevin
2
Então, deixe-me sugerir que você formule com mais clareza o seu OP: dentro de um determinado ambiente de shell, não posso digitar uma nova linha dentro de uma string entre aspas, como no eco "a [return] b".
dan

Respostas:

22

É uma má ideia (ter caracteres estranhos nos nomes dos arquivos), mas você pode fazer

 mv somefile.txt "foo
 bar"

(você também pode ter feito mv somefile.txt "$(printf "foo\nbar")"ou mv somefile.txt foo$'\n'bar, etc ... detalhes são específicos do seu shell. Estou usando zsh)

Leia mais sobre globbing , por exemplo, glob (7) . Os detalhes podem ser específicos do shell. Mas entenda que /bin/mv é fornecido (pelo seu shell), via execve (2) , uma matriz expandida de argumentos: expansão de argumentos e globbing são de responsabilidade do shell de chamada.

E você pode até codificar um pequeno programa C para fazer o mesmo:

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

Salve o programa acima foo.c, compile-o gcc -Wall foo.c -o foo e execute./foo

Da mesma forma, você pode codificar um script semelhante em Perl, Ruby, Python, Ocaml, etc.

Mas isso é uma péssima ideia. Evite novas linhas nos nomes de arquivos (isso confundirá o usuário e poderá quebrar muitos scripts).

Na verdade, eu recomendo usar apenas letras, dígitos e +-/._% caracteres não acentuados ( / sendo o separador de diretório) nos caminhos dos arquivos. Arquivos "ocultos" (começando com .) devem ser usados ​​com cautela e parcimônia. Acredito que usar qualquer tipo de espaço em um nome de arquivo seja um erro. Use um sublinhado (por exemplo foo/bar_bee1.txt) ou um sinal de menos (por exemplo foo/bar-bee1.txt)

Basile Starynkevitch
fonte
Sim, eu sei que posso copiar e colar um caractere de nova linha, mas como eu poderia fazer isso sem copiar e colar?
AB
4
Não há copiar ou colar aqui. Existe uma nova linha digitada depois foo.
dan
No entanto, ele também funciona copiando e colando-o
kos
1
É uma má idéia para arquivos comuns, mas um bom caso de teste, como o OP mencionou.
Artdanil 29/06/2015
5
@e ,seria mais inofensivo do que -(como na primeira posição) ou %. Todas essas recomendações são principalmente para solucionar bugs em alguns aplicativos (que quebram em nomes de arquivos que são válidos do ponto de vista do SO). É um retrocesso pedir às pessoas que não usem esses caracteres em vez de corrigir seus erros.
Stéphane Chazelas
19

Se você usar o bash, este comando deve funcionar.

mv a $'b\nc'
favadi
fonte
4
Funciona zshtambém com =)
AB
3
bash, como o zsh e o FreeBSD copiaram isso do ksh93.
Stéphane Chazelas
Muito obrigado! Eu esqueci completamente cerca de US $ em bash, embora fosse a minha própria resposta em SO aconselhando a usá-lo - stackoverflow.com/questions/1825552/grep-a-tab-in-unix/...
antimirov
6

Eu sei que você pediu uma mvsolução, no entanto, apesar do aviso, isso pode ser feito facilmente com rename(no pacote 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
fonte
Obrigado pela renameboa ideia. +1
AB
5

Um aspecto desse problema não é realmente awk- e apenas um pouco sobre o shell. O problema é que em um tty canônico padrão, na maioria das vezes, a disciplina tty do kernel está armazenando sua entrada em buffer - apenas ecoando-a na tela e em nenhum outro lugar - para que ele possa lidar com eficiência no backspacing e afins.

No entanto, quando você pressiona return ou insere uma nova linha, todos esses dados em buffer são enviados de uma só vez para o aplicativo de leitura - geralmente seu shell. Você pode observar isso observando $PS2depois de inserir uma cotação pendente. Quando o shell é impresso $PS2, é porque ele acabou de ler algum bloco de sua entrada e ainda não está convencido de que você terminou.

Portanto, por conveniência, o que você precisa é de uma maneira de enviar uma linha de \new para o buffer do terminal sem precisar enviar todas as outras entradas imediatamente. A maneira padrão de fazer isso é com a sequência de teclas CTRL+V- que cita para o terminal seu próximo caractere de entrada. Faça CTRL+Visso CTRL+J- porque o último é geralmente como digitar um \newline literal . Você saberá que funcionou quando não vê, $PS2porque o shell ainda não leu sua entrada.

Nota no entanto, que quando se lê que a entrada de seu mais cedo CTRL+Vterá feito nenhuma diferença para o shell em tudo - que só cita-lo para a linha-disciplina. Você definitivamente vai querer citar a nova linha, além de fazer algo significativo com ela.

A propósito, CTRL+Vpode ser útil de outras maneiras - por exemplo, "$(printf \\33)"não é a única maneira de escrever um ESCpersonagem em um shell script - e nem é a mais simples. Você pode literalmente digitar qualquer caractere que o teclado enviará sem que o driver de entrada tente interpretá-lo se você apenas escapar dessa maneira.

Costumo usar <tab> s na linha de comando sem que o shell tente concluir nada. Como os shells que fazem a conclusão normalmente configuram o <tab> de uma maneira sinônimo de stty eol \t, para fazer com que seus sistemas de conclusão funcionem, CTRL+Vfunciona para mim mesmo em ambientes desconhecidos.

mikeserv
fonte
2
Obrigado por mencionar 'Ctrl + V'. Eu estava prestes a postar uma resposta e depois vi sua explicação.
Artdanil 29/06/2015