Como determinar o nome do arquivo de trabalho atual em uma operação regex?

4

Se eu tentar executar uma operação regex em arquivos como (exemplo):

$ mv *.ext *.new.ext

Isso não funciona.

Como posso garantir que quando eu renomeio em lote ou executo uma operação em vários arquivos que eles mantêm seu nome atual (não a extensão, apenas o nome)?

Nota: Eu não estou procurando por algo como o comando rename porque isso tem que funcionar para tudo , não apenas renomeando arquivos.

Nota 2: Ok, essas respostas são boas e explicam coisas que eu não entendi antes. Obrigado!

O comando em questão que estou tentando usar chama-se firmtool e é usado na cena homebrew do 3DS.

Embala arquivos .bin em arquivos .firm e eu gostaria que ele fosse executado em todos os arquivos .bin em um diretório, empacotando-os enquanto preservava o nome original.

Aqui está a sintaxe de que preciso para o comando firmtool :

firmtool build test.firm -n 0x23F00000 -e 0 -D arm9loaderhax.bin -A 0x23F00000 -C NDMA

onde test.firm é a saída e arm9loaderhax.bin é a entrada.

Desejo executar este comando se eu tiver um diretório com test1.bin e test2.bin que produza test1.firm e test2.firm . Por favor, deixe-me saber se eu posso usar a expressão regular ou um loop para concluir este comando. Obrigado por qualquer ajuda!

    
por qwertyatom100 21.05.2017 / 07:14

2 respostas

5

Hmm. Você não pode 1 , nem universalmente.

* é um curinga. O shell expande . Quando você digita

*.ext

O shell o expande para uma lista de todos os arquivos correspondentes, como:

1.ext 2.ext whatever.ext

Isso é o que é passado para o comando, no seu caso mv . Se mv receber mais de dois argumentos, o último deve ser um diretório, então

mv *.ext *.new

falha com *.new não é um diretório, porque mv está recebendo uma longa lista de argumentos.

O próprio shell não suporta regex, mas o Linux tem muitos utilitários como o renomeio do perl ...

rename -n 's/(.*)\.ext/.new/' *

E remova -n após o teste para realmente renomear ...

Mas você pode capturar os nomes variáveis em uma variável e usá-la para fazer um loop pelos arquivos ...

for f in *.ext; do echo mv -v -- "$f" "${f/.ext/.new}"; done

e remova echo após o teste para concluir a renomeação ... Você pode usar praticamente qualquer comando com for , não apenas mv . Aqui está um exemplo ligeiramente diferente com base na sua pergunta:

for f in *.bin; do
  firmtool build "${f%.bin}.firm" -n 0x23F00000 -e 0 -D "$f" -A 0x23F00000 -C NDMA
done

Nesse caso, ${f%.bin} será expandido para o valor da variável f com o sufixo .bin retirado (se tiver esse sufixo; caso contrário, ele se expandirá para o valor de f ).

Consulte o manual em Expansão do Parâmetro do Shell para outros tipos de expansão .

1 aguarda comprovação errada

    
por Zanna 21.05.2017 / 07:38
1

O que acontece e por que o comando mv falha

Vamos realmente examinar o que acontece sob o capô usando strace .

$ touch file{1,2}.ext
$ strace -e trace=execve mv *.ext *.new.ext            
execve("/bin/mv", ["mv", "file1.ext", "file2.ext", "*.new.ext"], [/* 80 vars */]) = 0
mv: target '*.new.ext' is not a directory
+++ exited with 1 +++

Como Zanna explicou em sua resposta *.ext será expandido pelo shell para todos os arquivos no diretório de trabalho atual que terminam em .ext string. O que você vê nesse exemplo é que *.new.ext não é expandido, exatamente porque não há arquivo que corresponda a esse padrão, por exemplo. não há arquivo foo.new.ext em qualquer lugar, portanto o shell passa diretamente como argumento.

O comando mv , por sua vez, trata isso como a seguinte sintaxe:

mv [OPTION]... SOURCE... DIRECTORY

Se você tivesse um diretório existente no último argumento, isso funcionaria

$ mv *.ext test_dir

$ tree       
.
└── test_dir
    ├── file1.ext
    └── file2.ext

O que você realmente quer fazer

Seu objetivo original, no meu entender, é renomear cada arquivo *.ext para o arquivo correspondente com .new.ext ending. Isso pode ser feito com renomear, como mostra o Zanna, ou através de looping no shell:

$ for f in ./*.ext ; do mv "$f" "$(basename "$f")".new.ext; done                         

$ ls
file1.ext.new.ext  file2.ext.new.ext

Observe o uso de basename para extrair a parte antes de .ext e o uso de ./*.ext . Use if ./* se destina a evitar problemas com nomes de arquivos que podem ter o - inicial e é geralmente uma abordagem recomendada, em vez de *

Notas adicionais

Na sua pergunta, você afirmou:

  

Se eu tentar executar uma operação regex em arquivos

Isso faz parte do problema. * no shell é Expansão do nome do caminho, que se expande para a lista de arquivos no diretório de trabalho atual. Ele não funciona no mesmo sentido da expressão regular abc.* , onde você combina a string começando com abc com qualquer último caractere zero ou mais vezes.

  

Como posso garantir que quando eu renomeio em lote ou executo uma operação em vários arquivos que eles mantêm seu nome atual (não a extensão, apenas o nome)?

Você pode iterar a lista de arquivos usando um for loop e extrair ativamente o nome do arquivo usando o comando basename , como mostrei acima. prename pode ser usado, se você definir corretamente as substituições na expressão regex. O que é legal sobre prename é que ele tem funcionalidade de execução a seco (a opção -n ), em que nenhuma renomeação real é executada. A mesma ideia pode ser usada com iteração, em que você usa echo em vez de mv primeiro.

  

Eu não estou procurando por algo como o comando rename, porque isso tem que funcionar para tudo, não apenas renomeando arquivos.

Novamente, * no que diz respeito ao shell lida com arquivos no diretório atual. Isto não é o mesmo que expressões regulares. Sua abordagem deve ser adaptada de acordo com as ferramentas que você usa.

    
por Sergiy Kolodyazhnyy 21.05.2017 / 11:52