Como renomear o padrão em um arquivo para outro através de vários diretórios recursivamente

1

Eu tenho vários diretórios através dos quais eu quero iterar recursivamente e alterar o nome do arquivo

*.GEOT14246.*

para

*.GEOT15000.*

Existe um forro no script bash para fazer isso ou eu tenho que escrever um script com um loop for. Minha tentativa (que não funciona) está abaixo:

#!/bin/bash

for file in 'find . -type f -name "*.GEOT14246.*"'  
do
    echo "file = $file"
    mv $file *.GEOT15000.*
done

Eu então ligo com:

cd /path/to/script/
sh ./script1.sh /path/to/starting/dir

Obviamente, isso não funciona porque não acho que estou passando o caminho do diretório inicial como um argumento. Eu sou muito novo no Unix, então como posso passar argumentos para ele dizer de qual diretório ele deve começar a pesquisar e como faço para que o script funcione?

    
por Knab 24.05.2013 / 10:01

3 respostas

3

Isso deve funcionar:

find . -type f -name "*.GEOT14246.*" -print0 | \
xargs -0 rename 's/GEOT14246/GEOT15000/'

, pois não há diretórios nomeados *.GEOT14246.*

Uma variante bash usando find pode ser algo como:

while IFS= read -r -d $'
${some_variable/find/replace}
' file; do printf "MV: %-40s => %s\n" "$file" "${file/GEOT14246/GEOT15000}" mv "$file" "${file/GEOT14246/GEOT15000}" done < <(find . -type f -name "*.GEOT14246.*" -print0) | | +--- this is starting directory +--- This ensures no hiccup if newline etc. in name

Caminhos relativos, mas cheios, são passados de find - o que você deve ver a partir do printf declaração.

O novo nome é compilado usando o bash:

${some_variable//find/replace}

para substituir todo o uso de find :

chmod +x script_file

Mais aqui . Isso também pode ser uma boa leitura: BashPitfalls , UsingFind .

Leia alguns guias como o BashGuide . Encontre alguns tutoriais on-line, mas geralmente nunca confie neles, pergunte aqui ou no irc.freenode.net #bash.

Você não precisa invocar o script chamando sh . Isso também chamaria shell Bourne (sh) e não shell Bourne Again (bash). Se você pretende executá-lo com bash; problema bash file . A extensão .sh também está fora do lugar.

O que você normalmente faz é tornar o arquivo executável por:

./script_file

e, em seguida, execute-o com:

# Check if argument 1 is a directory, if not exit with error
if ! [[ -d "$1" ]]; then
    printf "Usage: %s [PATH]\n" "$(basename "$0")" >&2
    exit 1
fi

# OK
path="$1"

while ...

done < <(find "$path" -type f ...)

O shebang cuida de qual ambiente o script deve ser executado.

No seu script, você não usa o nome do caminho passado em nenhum lugar. O script tem uma "lista de argumentos" a partir de $0 , que é o nome do script, $1 primeiro argumento, $2 segundo, - e assim por diante.

No seu script, você faria algo na direção de:

$ tree
.
└── d1
    ├── a1
    │   ├── a2
    │   │   ├── a3
    │   │   │   └── foo.GEOT14246.faa
    │   │   └── foo.GEOT14246.faa
    │   └── foo.GEOT14246.faa
    └── b1
        ├── b2
        │   ├── b3
        │   │   └── foo.GEOT14246.faa
        │   └── foo.GEOT14246.faa
        └── foo.GEOT14246.faa

Sua declaração mv atual moveria todos os arquivos para onde quer que você emitisse o comando - para um arquivo chamado .GEOT14246. : (como na sobrescrita para cada instrução mv ):

Antes da execução do script:

$ tree
.
├── d1
│   ├── a1
│   │   └── a2
│   │       └── a3
│   └── b1
│       └── b2
│           └── b3
└── *.GEOT15000.*

Após a execução do script:

find . -type f -name "*.GEOT14246.*" -print0 | \
xargs -0 rename 's/GEOT14246/GEOT15000/'

Também arquivos / caminhos com espaços ou outras coisas engraçadas tornariam o script ruim e espalharia o caos. Portanto, você deve citar suas variáveis (como "$file" ).

    
por 24.05.2013 / 10:19
2

No bash 4.0 ou posterior, você também pode usar o globstar:

shopt -s globstar
for f in **/*GEOT14246*; do mv "$f" "$f{/14246/15000}"; done
rename s/14246/15000/ **/*GEOT14246*
  • O padrão glob também corresponde a pastas que contêm GEOT14246
  • Ambas as opções se aplicam a todo o caminho
  • Pode haver um erro muito longo na lista de argumentos se o padrão glob for expandido para ser maior que getconf ARG_MAX

Ou usando encontrar e ler:

find . -name '*GEOT14246*' | while read f; do mv "$f" "${f/14246/15000}"; done
  • -print0 não é necessário se os caminhos não contiverem alimentações de linha
  • IFS= não é necessário se os caminhos não forem iniciados ou terminarem com caracteres no IFS
  • -r não é necessário se os caminhos não contiverem barras invertidas
  • < <() não é necessário se você não modificar variáveis fora do loop while ou algo
por 25.05.2013 / 18:11
1

Não existe um simples one-liner no bash, mas existe um em zsh. Adicione a linha autoload -U zmv ao seu ~/.zshrc . Então você pode usar zmv e zsh's wildcards e substituição de parâmetros .

zmv '/path/to/starting/dir/**/*GEOT14246*' '${f//GEOT14246/GEOT15000}'

Observe que isso substitui GEOT14246 nos nomes dos arquivos também. Você precisa das aspas em torno dos dois argumentos porque zmv os analisa novamente.

No Linux, com bash ou zsh, você pode usar rename . Isso substitui apenas a primeira ocorrência de GEOT14246 em cada caminho.

rename GEOT14246 GEOT15000 /path/to/starting/dir/**/*GEOT14246*

No bash, você precisa ativar o ** recursive glob primeiro com shopt -s globstar (adicione esta linha ao seu ~/.bashrc ).

Se a sua distribuição Linux é Debian, Ubuntu ou um derivado, o % co_de O comando% é diferente. Use um destes:

rename.ul GEOT14246 GEOT15000 /path/to/starting/dir/**/*GEOT14246*
rename 's/GEOT14246/GEOT15000/g' /path/to/starting/dir/**/*GEOT14246*
    
por 26.05.2013 / 01:36