Substituir a primeira coluna do arquivo pela saída do comando

5

A partir desta pergunta copie e substitua a coluna usando unix

Eu tentei criar uma solução que funcione apenas nesse arquivo:

20070101 10.2317  79.1638   6.0  26.7  20.9   0.8  14.0  98.6
20070102 10.2317  79.1638   5.6  26.5  20.8   1.9  13.6  98.0
20070103 10.2317  79.1638   7.5  27.7  20.8   0.1  15.8  96.4
20070104 10.2317  79.1638   8.1  26.0  19.6   0.0  15.5  94.1

E obtenha a saída:

01/01/2007  10.2317   79.1638   6.0  26.7  20.9   0.8  14.0  98.6
02/01/2007  10.2317   79.1638   5.6  26.5  20.8   1.9  13.6  98.0
03/01/2007  10.2317   79.1638   7.5  27.7  20.8   0.1  15.8  96.4
04/01/2007  10.2317   79.1638   8.1  26.0  19.6   0.0  15.5  94.1

Sem outros arquivos envolvidos.

Por isso, quero substituir a primeira coluna pela data convertida.

Eu recebi a data do arquivo original com:

$ awk '{print $1}' filedate.txt
20070101
20070102
20070103
20070104

Então fiz a conversão de data com:

for i in $(awk '{print $1}' filedate.txt); do date -d "$i"  +%d/%m/%Y; done
01/01/2007
02/01/2007
03/01/2007
04/01/2007

Mas não consegui modificar a primeira coluna do arquivo pelos valores de data já convertidos. Eu tentei com substituição awk ( awk '{$1=$dt}1' ):

for i in $(awk '{print $1}' filedate.txt); do dt=$(date -d "$i" +%d/%m/%Y) && awk '{$1=$dt}1' filedate.txt; done

Mas como há um loop envolvido, a saída não é a desejada.

Como posso conseguir isso com awk ? É possível fazer o mesmo com sed ?

EDITAR

Nos comentários da outra pergunta, vi o seguinte caminho com sed

 sed 's,^\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\),//,'

Mas agora estou curioso sobre como fazer isso envolvendo o comando date .

    
por Zumo de Vidrio 18.01.2017 / 13:12

6 respostas

2

Vamos usar o seu loop já existente (que eu realmente não vi, mas que parece fazer o seu trabalho):

for i in $(awk '{print $1}' filedate.txt); do date -d "$i"  +%d/%m/%Y; done

Depois, dobre minha resposta a essa outra pergunta , com pequenas modificações:

for i in $(awk '{print $1}' filedate.txt); do date -d "$i"  +%d/%m/%Y; done |
paste - <( cut -d ' ' -f 2- filedate.txt )

Resultado:

01/01/2007      10.2317  79.1638   6.0  26.7  20.9   0.8  14.0  98.6
02/01/2007      10.2317  79.1638   5.6  26.5  20.8   1.9  13.6  98.0
03/01/2007      10.2317  79.1638   7.5  27.7  20.8   0.1  15.8  96.4
04/01/2007      10.2317  79.1638   8.1  26.0  19.6   0.0  15.5  94.1

Mais curto, sem o loop:

date -f <( cut -d ' ' -f 1 filedate.txt ) +"%d/%m/%Y" |
paste - <( cut -d ' ' -f 2- filedate.txt )

Sem o pipe:

paste <( date -f <( cut -d ' ' -f 1 filedate.txt ) +"%d/%m/%Y" ) \
      <( cut -d ' ' -f 2- filedate.txt )

Todos esses exemplos obviamente requerem bash ou ksh ou qualquer outro shell que entenda substituições de processos. O% GNUdate também é necessário.

Veja minha resposta a essa outra pergunta para uma explicação de como isso funciona.

    
por 18.01.2017 / 13:48
2

Se a transformação que você deseja é simplesmente uma reordenação das informações existentes, por que não fazer

awk '{ $1=sprintf("%02i/%02i/%04i",
     substr($1, 7, 2), substr($1, 5, 2), substr($1, 1, 4)) }1' file

Estamos levantando substrings do primeiro campo e remontando-os em um novo valor para o primeiro campo, depois imprimindo toda a linha de entrada como de costume. (O único 1 após a chave de fechamento é uma expressão padrão do Awk para impressão incondicional.)

Você provavelmente se arrependerá de converter perfeitamente boas datas legíveis por máquina para "legível por humanos" muito em breve.

    
por 18.01.2017 / 13:43
2

Se você tiver o GNU awk ( gawk ), você pode substituir uma coluna usando a saída de comando usando o formulário getline / variable / pipe de getline para desembolsar para a função date :

gawk '{"date +%d/%m/%Y -d" $1 | getline $1} 1' file

No entanto, se você quiser apenas alterar o formato de data de uma coluna, poderá fazer isso de forma nativa usando as funções internas mktime e strftime :

gawk '{
  d = sprintf("%d %02d %02d 0 0 0", substr($1,1,4), substr($1,5,2), substr($1,7,2));
  t = mktime(d);
  $1 = strftime("%d/%m/%Y", t);
  } 1' file

embora, nesse caso, você possa fazer a conversão necessária usando a manipulação de string simples (que deve funcionar em qualquer sabor de awk ):

$ mawk '{$1 = sprintf("%02d/%02d/%02d", substr($1,7,2), substr($1,5,2), substr($1,1,4))} 1' file
01/01/2007 10.2317 79.1638 6.0 26.7 20.9 0.8 14.0 98.6
02/01/2007 10.2317 79.1638 5.6 26.5 20.8 1.9 13.6 98.0
03/01/2007 10.2317 79.1638 7.5 27.7 20.8 0.1 15.8 96.4
04/01/2007 10.2317 79.1638 8.1 26.0 19.6 0.0 15.5 94.1
    
por 18.01.2017 / 14:25
2
sed 's| *\(....\)\(..\)\(..\)|//|' < in > out
    
por 18.01.2017 / 15:09
1

Embora possa não parecer a melhor prática, eu iria para uma solução diferente, ou seja, usando o tratamento de parâmetros bash como este:

gv@debi64:$ a="20070101"; b="${a: -2:2}/${a: -4:2}/${a: 0:4}";echo $b
01/01/2007

Eu poderia ler o arquivo e usar algo como sed -i "s/$a/$b/g" i poderia alcançar o que você precisa:

gv@debi64:$ cat a.txt
20070101 10.2317  79.1638   6.0  26.7  20.9   0.8  14.0  98.6
20070102 10.2317  79.1638   5.6  26.5  20.8   1.9  13.6  98.0
20070103 10.2317  79.1638   7.5  27.7  20.8   0.1  15.8  96.4
20070104 10.2317  79.1638   8.1  26.0  19.6   0.0  15.5  94.1

gv@debi64:$ while IFS=" " read -r df rest;do ndf="${df: -2:2}/${df: -4:2}/${df: 0:4} "; sed -i "s#$df#$ndf#g" a.txt;done <a.txt

gv@debi64:$ cat a.txt
01/01/2007  10.2317  79.1638   6.0  26.7  20.9   0.8  14.0  98.6
02/01/2007  10.2317  79.1638   5.6  26.5  20.8   1.9  13.6  98.0
03/01/2007  10.2317  79.1638   7.5  27.7  20.8   0.1  15.8  96.4
04/01/2007  10.2317  79.1638   8.1  26.0  19.6   0.0  15.5  94.1
    
por 18.01.2017 / 13:39
1

O modificador e val do GNU sed para o comando s permitirá que você use date para converter as datas conforme necessário:

sed -r 's|(\S+)(.*)|date -d  "+%d/%m/%y "|e'

A expressão substituída é um comando de data bem formado. O modificador e faz com que isso seja executado para cada linha e o buffer padrão (e, portanto, a saída) seja substituído pela saída de cada comando de data.

    
por 18.01.2017 / 19:43