Modifique o formato da data no local através do comando 'sed'

6

Estou tentando modificar as datas em um texto no lugar onde há 2016-Dec-24 ou 2016-12-24 e tenho que substituí-las como 24/12/2016 .

Existe alguma maneira de usar sed -i para fazer isso?

Exemplo de entrada:

Holiday or Observance Religious Group Dates Additional Notes *Rosh Hashanah (New Year) Jewish 25-Sep-2014 - 26-Sep-2014 Designated non-work day for observers Navaratri/Dassehra Hindu Sept. 2014-Oct-03

*Yom Kippur (Day of Atonement) Jewish 2014-Oct-04, Designated non-work day for observers

Sponsored Conferences Title Location Conference Date 2017 IEEE Sensors Applications Symposium (SAS) United States 2017-03-13, 2017-03-14, 2017-03-15 2017 IEEE International Symposium on Medical Measurements and Applications (MeMeA) United States 2017-05-07 to 2017-05-10 2017 IEEE International Instrumentation and Measurement Technology Conference (I

Isso é o que eu tentei até agora.

sed -i -e 's/\([0-9]\{4\}\)-\(01|Jan\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(02|Feb\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(03|Mar\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(04|Apr\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(05|May\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(06|Jun\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(07|Jul\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(08|Aug\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(09|Sep\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(10|Oct\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(11|Nov\)-\([0-9]\{2\}\)/\/\//g' $1
sed -i -e 's/\([0-9]\{4\}\)-\(12|Dec\)-\([0-9]\{2\}\)/\/\//g' $1
    
por Alina Gorgovan 19.12.2016 / 11:19

3 respostas

5

Se você tiver acesso ao GNU date (o padrão nos sistemas Linux), você pode fazer:

$ sed -E 's/(.*)-([a-z]+)(.+)/-/i' file | 
    while read d; do date -d "$d" +%d/%m/%y; done
24/12/16
24/12/16

Isso altera linhas como 2016-Dec-24 a Dec-24-2016 (um formato que o GNU date pode entender), deixa linhas como 2016-12-24 (um formato GNU date já entende) sozinho e passa cada linha como uma entrada data string para date . Ele não faz isso no local e não usa sed -i , mas é quase certamente a abordagem mais simples.

Se você realmente precisa fazer isso usando sed , você pode fazer uma lista de todos os meses e números correspondentes:

$ for m in {1..12}; do printf '%s %s\n' "$m" $(date -d "$m/1/2016" +%b); done
1 Jan
2 Feb
3 Mar
4 Apr
5 May
6 Jun
7 Jul
8 Aug
9 Sep
10 Oct
11 Nov
12 Dec

Salvar como months e, em seguida, iterar sobre ele para modificar seu arquivo:

while read num mon; do 
    sed -Ei "s/$mon/$num/; s#(.*)-(.*)-(.*)#//#" file
done < months 

Ou, se a sua implementação sed precisar separar -e :

while read num mon; do 
    sed -i -e "s/$mon/$num/" -Ee 's#(.*)-(.*)-(.*)#//#' file
done < months 

A primeira substituição substituirá todos os nomes dos meses alfabéticos pelo número correspondente e a segunda move as coisas para obter o formato desejado.

    
por 19.12.2016 / 11:58
2

A única coisa que vejo errado na regex é que no modo básico (BRE), | é literal - você precisa de \| para torná-lo um OR lógico, ou seja, \(01\|Jan\) e assim por diante.

Se a sua versão for compatível com -e , não vejo nenhuma boa razão para fazer várias chamadas para sed - você pode apenas encadear o -e <expr1> -e <expr2> ... em uma única chamada. Então

sed -i \
  -e 's/\([0-9]\{4\}\)-\(01\|Jan\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(02\|Feb\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(03\|Mar\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(04\|Apr\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(05\|May\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(06\|Jun\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(07\|Jul\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(08\|Aug\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(09\|Sep\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(10\|Oct\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(11\|Nov\)-\([0-9]\{2\}\)/\/\//g' \
  -e 's/\([0-9]\{4\}\)-\(12\|Dec\)-\([0-9]\{2\}\)/\/\//g' $1

No entanto, existem maneiras mais elegantes de fazer isso - por exemplo, em perl usando strptime e strftime functions (fornecidas pelo módulo Time::Piece , por exemplo):

perl -i -MTime::Piece -pe '
  s|\d{4}-\d\d-\d\d?|Time::Piece->strptime($&, "%Y-%m-%d")->strftime("%Y/%m/%d")|ge;
  s|\d{4}-...-\d\d?|Time::Piece->strptime($&, "%Y-%b-%d")->strftime("%Y/%m/%d")|ge;
' file
    
por 19.12.2016 / 20:43
0
sed -r -i '
/([0-9]{4})-([[:alpha:]]{3})-([0-9]{2})/ {
    s#([0-9]{4})-Jan-([0-9]{2})#/01/#g
    s#([0-9]{4})-Feb-([0-9]{2})#/02/#g
    s#([0-9]{4})-Mar-([0-9]{2})#/03/#g
    s#([0-9]{4})-Apr-([0-9]{2})#/04/#g
    s#([0-9]{4})-May-([0-9]{2})#/05/#g
    s#([0-9]{4})-Jun-([0-9]{2})#/06/#g
    s#([0-9]{4})-Jul-([0-9]{2})#/07/#g
    s#([0-9]{4})-Aug-([0-9]{2})#/08/#g
    s#([0-9]{4})-Sep-([0-9]{2})#/09/#g
    s#([0-9]{4})-Oct-([0-9]{2})#/10/#g
    s#([0-9]{4})-Nov-([0-9]{2})#/11/#g
    s#([0-9]{4})-Dec-([0-9]{2})#/12/#g
}
s#([0-9]{4})-([0-9]{2})-([0-9]{2})#//#g
' file
    
por 19.01.2017 / 05:13