Script Bash para renomear arquivos de uma fonte de arquivo de texto

6

Sou relativamente novo para bater; Eu posso executar tarefas administrativas simples com comandos simples 1 por vez. No entanto, eu tenho a tarefa de renomear alguns arquivos em um diretório usando um arquivo de texto como fonte para minha renomeação e realmente aprecio alguns ponteiros, pois estou bem fora de minha profundidade.

Deixe-me explicar:

New File Name.xlsx 0.1  000011F4.dat 
New File Name.xlsx 0.2  000011F5.dat 
New File Name.xlsx 0.3  000011F6.dat 
New File Name.xlsx 0.4  000011F7.dat 
New File Name.xlsx 0.5  000011F8.dat 
New File Name.xlsx 0.6  000011F9.dat 

O arquivo de texto de origem eu me assemelho um pouco acima. A intenção é que a primeira 'coluna' seja o novo nome do arquivo, o meio é a versão e o terceiro é o nome do arquivo atual.

Eu preciso renomear os arquivos .dat no diretório, alterando-os para os nomes apresentados na primeira coluna. Eu também preciso prefixar o número da versão 0.1, 0.2 etc ... para o início de cada arquivo.

Eu tenho algumas perguntas:

É um grande problema que os arquivos tenham espaços em branco neles? Seria melhor adicionar "" em torno de cada string de arquivo?

Basicamente, não tenho ideia de por onde começar e qualquer ajuda seria muito apreciada. Como você pode ver, é um pouco mais complexo do que uma renomeação comum, dando a necessidade de adicionar a coluna de versão ao início do nome do arquivo e ao espaço em branco na lista.

    
por user2472419 08.08.2013 / 19:42

4 respostas

7

Isso deve funcionar:

sh <(sed -r 's/^\s*(.*)\s+([0-9\.]+)\s+([0-9A-Z]{8}\.dat)\s*$/mv -iv  " "/' files)

... onde files é o nome do seu arquivo de origem.

O que isto faz é passar o resultado do comando sed para uma nova instância de sh (o shell), usando processo de substituição . A saída do comando sed é:

mv -iv 000011F4.dat "0.1 New File Name.xlsx"
mv -iv 000011F5.dat "0.2 New File Name.xlsx"
mv -iv 000011F6.dat "0.3 New File Name.xlsx"
mv -iv 000011F7.dat "0.4 New File Name.xlsx"
mv -iv 000011F8.dat "0.5 New File Name.xlsx"
mv -iv 000011F9.dat "0.6 New File Name.xlsx"

Separando o comando sed , ele procura um padrão:

  • ^ - o começo da linha
  • \s* - qualquer espaço em branco no início
  • (.*) - qualquer caractere (os parênteses armazenam o resultado em )
  • \s+ - pelo menos um caractere de espaço em branco
  • ([0-9\.]+) - pelo menos um dos 0-9 e . (armazenados em )
  • \s+ - pelo menos um caractere de espaço em branco
  • ([0-9A-Z]{8}\.dat) - 8 caracteres em 0-9 ou A-Z , seguidos por .dat (armazenados em )
  • \s* - qualquer espaço no final
  • $ - o fim da linha

... e o substitui por mv -iv " " , em que to são os valores armazenados anteriormente. Você pode usar algo diferente de um espaço entre o número da versão e o resto do nome do arquivo, se quiser.

Aqui está o resultado:

$ ls -l
total 60
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F4.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F5.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F6.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F7.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F8.dat
-rw-rw-r-- 1 z z   0 Aug  8 14:15 000011F9.dat
-rw-rw-r-- 1 z z 222 Aug  8 13:47 files
$ sh <(sed -r 's/^\s*(.*)\s+([0-9\.]+)\s+([0-9A-Z]{8}\.dat)\s*$/mv -iv  " "/' files)
'000011F4.dat' -> '0.1 New File Name.xlsx'
'000011F5.dat' -> '0.2 New File Name.xlsx'
'000011F6.dat' -> '0.3 New File Name.xlsx'
'000011F7.dat' -> '0.4 New File Name.xlsx'
'000011F8.dat' -> '0.5 New File Name.xlsx'
'000011F9.dat' -> '0.6 New File Name.xlsx'
$ ls -l
total 60
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.1 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.2 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.3 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.4 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.5 New File Name.xlsx
-rw-rw-r-- 1 z z   0 Aug  8 14:15 0.6 New File Name.xlsx
-rw-rw-r-- 1 z z 222 Aug  8 13:47 files
    
por 08.08.2013 / 20:04
4
sed 's/^\(.*\.xlsx\) \+\([[:digit:]]\+\.[[:digit:]]\+\) \+\(.[^ ]*\)/"" ""/' \
  <file_list | xargs -n 2 mv

Isso divide a linha na parte antes de .xlsx , que é a segunda parte do novo nome, que se torna acessível como . O agarra a versão e atribui a . Em seguida vem o antigo nome do arquivo, ignorando um espaço à direita.

Isto é um dado fornecido para mv como um argumento. O -n 2 garante que mv receba dois argumentos, o antigo e o novo nome do arquivo.

Os espaços não apresentam nenhum problema, o que complica a situação é que sua lista de entrada não está bem estruturada. Se as colunas fossem trocadas e os nomes dos arquivos citados, você poderia usar apenas xargs e mv , sem manipulação prévia.

    
por 08.08.2013 / 20:14
1

Os espaços no nome do arquivo e o uso de vários espaços entre algumas colunas tornam isso mais difícil, mas de modo algum intransponível.

Leia o arquivo de lista linha por linha. Normalmente, seria usado while IFS= read -r; do … , mas aqui pode ser mais robusto remover os espaços em branco iniciais e finais. Para cada linha:

  • Quebre cada linha em três partes. Uma maneira de fazer isso é com correspondência de expressões regulares. [[:space:]]+ corresponde a um ou mais caracteres de espaço em branco (espaço ou tabulação); [[:space:]]+ corresponde a um ou mais caracteres que não são espaços em branco. Grupos parênteses podem ser recuperados por meio da variável BASH_REMATCH .
    Outra maneira, menos conveniente aqui, seria com ${VAR##PATTERN} e ${VAR%PATTERN} remover um prefixo ou sufixo de uma variável respectivamente.
  • Finalmente, execute o movimento. Não se esqueça de registrar quaisquer erros.

Colocando tudo junto:

ret=0
while read line; do
  if [[ $line =~ (.*[^[:space:]])[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+) ]]; then
    new_name="${BASH_REMATCH[1]}"
    version="${BASH_REMATCH[2]}"
    old_name="${BASH_REMATCH[3]}"
    mv -- "$old_name" "$version$new_name" || ret=1
  else
    echo "Malformed line: $line"
  fi
done <name_list.txt
exit $ret
    
por 09.08.2013 / 02:09
0

Uma solução awk deve executar este comando:

awk '{print "/bin/mv", $NF, "\"" $(NF-1), gensub(/^([^.]+\.xlsx).*/, "\1", 1) "\"" | "bash" } ; END { close("bash") }' sourcefile

O comando anterior passa para o shell bash a saída do comando:

awk '{print "/bin/mv", $NF, "\"" $(NF-1), gensub(/^([^.]+\.xlsx).*/, "\1", 1) "\""}' sourcefile

que deve ser executado primeiro para se certificar de que é realmente o que você deseja executar! Este comando awk imprime, para cada linha no arquivo de origem, o comando /bin/mv , seguido pelo último campo delimitado por espaço em branco na linha, seguido por aspas duplas, seguidas pelo campo do penúltimo no a linha, seguida pelo resultado de substituir toda a linha por tudo pela string .xlsx , seguido por aspas duplas.

Aqui está uma variante que você pode preferir:

awk '{print "/bin/mv", $NF, "\"" "0." FNR, gensub(/^([^.]+\.xlsx).*/, "\1", 1) "\"" | "bash" } ; END { close("bash") }' sourcefile

A variável FNR é o número da linha (assim, você pode omitir do arquivo de origem as entradas 0,1, 0,2, 0,3, ...).

O espaço em branco nos nomes dos arquivos não é o que eu chamaria de um "problema enorme", mas eu recomendaria contra isso. Você poderia usar algo como essa versão final, que altera os espaços para sublinhados em seus novos nomes de arquivos:

awk '{print "/bin/mv", $NF, "0." FNR "_" gensub(" ","_", "g", gensub(/^([^.]+\.xlsx).*/, "\1", 1)) | "bash" } ; END { close("bash") }' sourcefile
    
por 09.08.2013 / 03:44