Renomeie vários arquivos com mv para alterar a extensão

27

Quero renomear arquivos para alterar sua extensão, procurando efetivamente realizar

mv *.txt *.tsv

Mas ao fazer isso, recebo:

* .tsv não é um diretório

Acho um pouco estranho que os 10 primeiros hits do google mvfuncionem assim.

Sander Van der Zeeuw
fonte
Você deve explicar como deseja renomear esses arquivos exatamente.
Totor 26/01
11
Bem, eu tenho arquivos com uma extensão de * .gappedPeak e quero renomeá-los para * .bed. todos eles estão no mesmo diretório que deve ser muito simples, mas um completo para o laço era necessário para me suceder
Sander Van der Zeeuw
O que você encontrou no Google se aplica ao MSDOS, eu acho.
tiktak
@devnull 's solução alternativa é realmente útil
lucasvw

Respostas:

22

Quando você emite o comando:

mv *.txt *.tsv

o shell, vamos assumir o bash, expande os curingas se houver algum arquivo correspondente (incluindo diretórios). A lista de arquivos é passada para o programa, aqui mv. Se nenhuma correspondência for encontrada, a versão não expandida será aprovada.

Novamente: o shell expande os padrões, não o programa.


Cargas de exemplos é talvez a melhor maneira, então vamos lá:

Exemplo 1:

$ ls
file1.txt file2.txt

$ mv *.txt *.tsv

Agora, o que acontece na mvlinha é que o shell se expande *.txtpara os arquivos correspondentes. Como não há *.tsvarquivos que não são alterados.

O mvcomando é chamado com dois argumentos especiais :

  • argc: Número de argumentos, incluindo o programa.
  • argv: Uma matriz de argumentos, incluindo o programa como primeira entrada.

No exemplo acima, isso seria:

 argc = 4
 argv[0] = mv
 argv[1] = file1.txt
 argv[2] = file2.txt
 argv[3] = *.tsv

O mvprograma verifica se o último argumento,, *.tsvé um diretório. Como não é, o programa não pode continuar, pois não foi projetado para concatenar arquivos. (Normalmente, mova todos os arquivos para um.) Nem crie diretórios por capricho.

Como resultado, ele interrompe e relata o erro:

mv: target ‘*.tsv’ is not a directory

Exemplo 2:

Agora, se você diz:

$ mv *1.txt *.tsv

O mvcomando é executado com:

 argc = 3
 argv[0] = mv
 argv[1] = file1.txt
 argv[2] = *.tsv

Agora, novamente, mvverifique se *.tsvexiste. Como não, o arquivo file1.txté movido para *.tsv. Ou seja: o arquivo é renomeado para *.tsvcom o asterisco e tudo.

$ mv *1.txt *.tsv
‘file1.txt’ -> ‘*.tsv’

$ ls
file2.txt *.tsv

Exemplo 3:

Se você disse:

$ mkdir *.tsv
$ mv *.txt *.tsv

O mvcomando é executado com:

 argc = 3
 argv[0] = mv
 argv[1] = file1.txt
 argv[1] = file2.txt
 argv[2] = *.tsv

Como *.tsvagora é um diretório, os arquivos acabam sendo movidos para lá.


Agora: usando comandos como some_command *.tsvquando a intenção é realmente manter o curinga, deve-se sempre citá-lo. Ao citar, você evita que os curingas sejam expandidos se houver correspondências. Por exemplo, digamos mkdir "*.tsv".

Exemplo 4:

A expansão pode ser vista ainda mais se você fizer, por exemplo:

$ ls
file1.txt file2.txt

$ mkdir *.txt
mkdir: cannot create directory ‘file1.txt’: File exists
mkdir: cannot create directory ‘file2.txt’: File exists

Exemplo 5:

Agora: o mvcomando pode e funciona em vários arquivos. Mas se houver mais de dois, o último terá que ser um diretório de destino. (Opcionalmente, você pode usar a -t TARGET_DIRopção, pelo menos para GNU mv.)

Então, tudo bem:

$ ls -F
b1.tsv  b2.tsv  f1.txt  f2.txt  f3.txt  foo/

$ mv *.txt *.tsv foo

Aqui mvseria chamado com:

 argc = 7
 argv[0] = mv
 argv[1] = b1.tsv
 argv[2] = b2.tsv
 argv[3] = f1.txt
 argv[4] = f2.txt
 argv[5] = f3.txt
 argv[6] = foo

e todos os arquivos acabam no diretório foo.


Quanto aos seus links. Você forneceu um (em um comentário), onde mvnão é mencionado, mas rename. Se você tiver mais links, poderá compartilhar. Assim como nas páginas de manual em que você afirma que isso é expresso.

Runium
fonte
30

Sei que isso não responde à sua pergunta, mas, se você estava procurando outra maneira de renomear os arquivos em comparação com o loop da solução alternativa, por que não usar find? Eu usei esse comando várias vezes para substituir as extensões de arquivos em grandes diretórios por centenas de milhares de arquivos. Isso deve funcionar em qualquer sistema compatível com POSIX:

find . -name "*.gappedPeak" -exec sh -c 'mv "$1" "${1%.gappedPeak}.bed"' _ {} \;

Divisão de comando:

  1. ' .' => caminho de pesquisa começando no diretório atual marcado por '. "

  2. -name=> define o nome da correspondência de localização (nesse caso, todos os arquivos que terminam com .gappedPeak)

  3. -exec => execute o seguinte comando em cada partida

  4. sh -c => 'exec' cria um ambiente de shell independente para cada correspondência

  5. mv "$1" "${1%.gappedPeak}.bed"=> mvprimeira variável (indicada por $ 1 ), que é o nome do arquivo atual, com o novo nome. Aqui eu faço uma correspondência de substring e apago; então pegue o primeiro var novamente, $ 1 e use %para excluir .gappedPeakda string. A .bedno final apenas encadeia a variável remanescente, que é agora apenas , com , criando o novo nome de ficheiro.test#.bedtest#.bed

  6. O sublinhado é um espaço reservado por US $ 0

  7. O {}é substituído por cada *.gappedPeaknome de arquivo ( ) encontrado pelo findcomando e se torna $ 1 no shcomando.

  8. \;marca o final do -execcomando. Você também pode usar ';'ou ";".

Exemplo:

[user@before]# ls -lh
total 0
-rw-r--r--. 1 root root 0 Jan 26 11:40 test1.gappedPeak
-rw-r--r--. 1 root root 0 Jan 26 11:40 test2.gappedPeak
-rw-r--r--. 1 root root 0 Jan 26 11:40 test3.gappedPeak
-rw-r--r--. 1 root root 0 Jan 26 11:40 test4.gappedPeak
-rw-r--r--. 1 root root 0 Jan 26 11:40 test5.gappedPeak

[user@after]# ls -lh
total 0
-rw-r--r--. 1 root root 0 Jan 26 11:40 test1.bed
-rw-r--r--. 1 root root 0 Jan 26 11:40 test2.bed
-rw-r--r--. 1 root root 0 Jan 26 11:40 test3.bed
-rw-r--r--. 1 root root 0 Jan 26 11:40 test4.bed
-rw-r--r--. 1 root root 0 Jan 26 11:40 test5.bed
devnull
fonte
Você poderia passar pelo comando que descreve o que cada parte faz?
precisa saber é o seguinte
Sinto muito por isso, atualizei minha postagem. Espero que ajude.
precisa saber é
11
Incrível, obrigado! Com um guia do bash e sua explicação, consegui tudo.
precisa saber é o seguinte
Sem problemas. Ainda bem que ajuda.
devnull
Funciona como um encanto. Aqui está outro exemplo simplificado para file.abc-> blub.xyzem vários sub dirs:find . -name "file.abc" -exec sh -c 'mv "$1" "$(dirname $1)/blub.xyz"' _ {} \;
Mahn
9

mv *.txt *.tsvnão funciona; mvpode renomear apenas um arquivo por vez. Você entendeu mal as explicações ou elas estão erradas.

mmve renamepode renomear vários arquivos de uma vez. Mas existem duas versões em renametorno das quais são chamadas de maneira diferente. Deve haver muitas perguntas sobre isso aqui.

Hauke ​​Laging
fonte
11
cyberciti.biz/tips/... O primeiro hit no google está dizendo que mv deve funcionar como isso é assim porque eu achei tão estranho
Sander Van der Zeeuw
3
@SanderVanderZeeuw Não sei o que você lê lá. Os exemplos usam rename, não mv.
Hauke ​​Laging
11
desculpe, eu colei o HTML errado. Este foi o udemy.com/blog/rename-a-file-in-linux
Sander Van der Zeeuw
3
@SanderVanderZeeuw Embarrassing. Essas pessoas oferecem cursos? Infelizmente, não vejo a possibilidade de contato lá. Você pode verificar facilmente se isso funciona. Mas mv *.txt *.tsv mv(geralmente) não vê *.txtou *.tsvos nomes de arquivo expandidos pelo shell. O número de arquivos para os quais esses curingas se expandem seria "aleatório". A única situação em que isso funciona é se houver um arquivo com o nome *.txtque será renomeado para (literalmente) *.tsv(sem citar a bashopção nullglobnão deve ser configurada).
Hauke ​​Laging
11
Ou pior. Se alguém tem um arquivo chamado por exemplo, foo.txte um chamado baz.tsva mv *.txt *.tsvirá substituir o existente .tsvarquivo ...
Runium
4

Por exemplo, se você tem asd.txte qwe.txtarquivos no diretório ao executar o comando mv *.txt *.tsv, ele tenta mover esses dois arquivos para um diretório chamado *.tsv. Como não existe esse diretório, ocorre um erro.

Esref
fonte
Obrigado, isso esclarece parcialmente. Ainda acho estranho ver tantas páginas de manual em que eles afirmam que o mv deve funcionar em vários.
Sander Van der Zeeuw
2

rename(1)

renameé um script perl de Larry Wall, o criador do perl. Ele usa um regex Perl e opera no nome do arquivo.

rename 's/\.txt$/.tsv/' *.txt

Instalação

Debian / Ubuntu

Se você precisar instalar renameno Debian / Ubuntu, poderá fazer

sudo apt install rename
Evan Carroll
fonte
0

Outra opção a considerar é usar:

cp -p *.txt *.tsv
rm -f *.txt
  • A primeira linha copia todos os *.txtarquivos para *.tsvpreservar seus atributos usando o -psinalizador.
  • A segunda linha remove todos os arquivos com o padrão *.txt
  • Este método requer que você tenha espaço em disco suficiente para armazenar temporariamente os arquivos de origem e de destino
  • Esse método é mais lento do mvque simplesmente altera o / directory / filename sem realmente mover os arquivos, a menos que estejam em uma partição diferente.
WinEunuuchs2Unix
fonte