Como extrair elementos específicos de um nome de arquivo?

6

Eu tenho vários arquivos no seguinte formato:

2014-11-19.8.ext
2014-11-26.1.ext
2014-11-26.2.blah.ext
2014-11-26_3.ext
2014-11-26.4.stuff_here.ext
2014-12-03.1. could be anything.ext
2014-12-032b.ext
2014-11-26 613 adva.ext

Meu objetivo é fazer uma iteração de toda a lista de arquivos e tirar a formatação de data de YYYY-MM-DD e armazená-la em uma variável no formato YYYYMMDD para processamento posterior (no meu caso, ela será empurrada para um comando touch ).

Então, normalmente eu combinaria com essa expressão regular: (\d{4})-(\d{2})-(\d{2}).*

Em seguida, use $1$2$3 para obter o padrão desejado, mas não sei como fazer isso em bash / zsh .

Como isso pode ser feito dentro de um script de shell?

    
por ylluminate 14.07.2017 / 17:26

6 respostas

8

Usando a expansão de parâmetros

$ touch 2014-11-19.8.ext 2014-11-26.1.ext
$ for f in *.ext; do d="${f:0:4}${f:5:2}${f:8:2}"; echo "$d"; done
20141119
20141126
  • ${f:0:4} significa 4 caracteres a partir do índice 0 e f é o nome da variável
  • substitua echo "$d" pelo seu código
por 14.07.2017 / 18:00
5

Para percorrer todos os arquivos no diretório atual e comparar seus nomes de arquivos com o padrão desejado, defina uma variável contendo as partes da data

for f in *
do 
  [[ $f =~ ^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])(.*) ]] && 
  yourvar="${BASH_REMATCH[1]}${BASH_REMATCH[2]}${BASH_REMATCH[3]}"
done

Isso usa % de cohde% do bash para usar a correspondência de expressão regular para coloque as peças de data na matriz BASH_REMATCH.

    
por 14.07.2017 / 17:58
3

Você pode fazer isso interativamente usando GNU sed :

$ sed 's/^\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}.*\)//g' stuff.txt

Para vários arquivos (se estiver no mesmo diretório e nenhum outro arquivo considerado no diretório):

for file in *
do
    if [ -f "$file" ]
    then
          sed 's/^\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\).*//g' "$file"
    fi
done
    
por 14.07.2017 / 17:43
2

Aqui está uma maneira de zsh de fazer isso, sem loops:

autoload -U zmv
zmv -n '([0-9](#c4))-([0-9](#c2))-([0-9](#c2))(*)' '$1$2$3$4'
  • [0-9](#c4) significa qualquer dígito repetido 4 vezes
  • $1 - $2 se refere a parênteses usados anteriormente
  • -n impede a execução (apenas impressões), remova este sinalizador se estiver satisfeito com o resultado

Como zsh cuida da globalização, todos os casos de canto (espaços em branco, caracteres especiais, etc.) devem ser considerados automaticamente.

    
por 14.07.2017 / 18:09
2

Se você está no GNU Coreutils, você tem isto:

$ date --date=2014-11-13 +"%Y%m%d"
20141113

No entanto:

$ date --date=2014-11-130ABCJUNK +"%Y%m%d"
date: invalid date ‘2014-11-130ABCJUNK’

Portanto, a tarefa é muito mais simples: extatualize os dez primeiros caracteres de cada YYYY-MM-DDetc nome do arquivo para obter a data e, em seguida, passe para date para reformatar.

Mas, se estivermos no GNU Coreutils, podemos pular o comando date porque touch tem exatamente a mesma opção --date=STRING .

for file in * ; do
  date=${file%${file##??????????}} # chop all but first ten
  touch --date=$date -- "$file"
done

Mas por que esse dez caractere cortando o POSIX portátil quando estamos contando com touch do GNU Coreutils?

for file in * ; do
  date=${file:0:10}
  touch --date=$date -- "$file"
done
    
por 15.07.2017 / 03:56
1

Tente a substituição de padrões:

${parameter/pattern/string}
O parâmetro

é o nome da base do arquivo. padrão é um traço. Neste / - para substituir padrão globalmente. string está vazia, desde que você queira excluir os traços.

mv "${f}" "${f//-/}"

Advertência: Eu não consegui isso funcionando com o caso de espaços na extensão.

    
por 14.07.2017 / 17:52