AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 700664
Accepted
schrodingerscatcuriosity
schrodingerscatcuriosity
Asked: 2022-04-28 16:10:44 +0800 CST2022-04-28 16:10:44 +0800 CST 2022-04-28 16:10:44 +0800 CST

Se a coluna tiver vários valores, copie a linha com cada valor separadamente

  • 772

Tenho um arquivo com o seguinte formato, cada coluna separada por tabs:

C1  C2  C3
a   b,c d
e   f,g,h   i
j   k   l
...

Agora preciso ter o número de linhas de acordo com o número de valores separados por vírgulas (se for o caso) na 2ª coluna. As linhas devem ter um dos valores e não os outros. O resultado seria este:

C1  C2  C3
a   b   d
a   c   d
e   f   i
e   g   i
e   h   i
j   k   l
...
...

Como isso é devido ao trabalho o mais rápido possível, acabei de fazer um script não faça isso em casa , lendo linha por linha com um while, devido à minha falta de habilidade no awk, ou não explorar outras soluções possíveis com outras ferramentas. O roteiro é o seguinte:

Estou revisando o roteiro enquanto isso

# DON'T DO THIS AT HOME SCRIPT
> duplicados.txt
while IFS= read -r line; do
  # get the value of the column of interest
  cues="$(echo "$line" | awk -F'\t' '{ print $18 }')"
  # if the column has commas then it has multiple values
  if [[ "$cues" =~ , ]]; then
    # count the commas
    c=$(printf "%s" "$cues" | sed 's/[^,]*//g' | wc -c)
    # loop according to the number of commas
    for i in $(seq $(($c + 1))); do
      # get each value of the column of interest according to the position
      cue="$(echo "$cues" | awk -F',' -v c=$i '{ print $c; ++c }')"
      # save the line to a file substituting the whole column for the value
      echo "$line" | sed "s;$cues;$cue;" >> duplicados.txt
    done
    continue
  fi
  # save the single value lines
  echo "$line" >> duplicados.txt
done < inmuebles.txt

Com isso obtenho o resultado desejado (até onde sei). Como você pode imaginar o script é lento e muito ineficiente. Como eu poderia fazer isso com awkou outras ferramentas?

Uma amostra dos dados reais é assim, sendo a coluna de interesse o número 18:

1409233 UNION   VIAMONTE    Estatal Provincial  DGEP    3321    VIAMONTE                            -33.7447365;-63.0997115 Rural Aglomerado    140273900   140273900-ESCUELA NICOLAS AVELLANEDA
1402961 UNION   SAN MARCOS SUD  Estatal Provincial  DGEA, DGEI, DGEP    3029, 3311, Z11 SAN MARCOS SUD                          -32.629557;-62.483976 / -32.6302699949582;-62.4824499999125 / -32.632417;-62.484932 Urbano  140049404, 140164000, 140170100, 140173100  140049404-C.E.N.M.A. N° 201 ANEXO SEDE SAN MARCOS SUD, 140164000-C.E.N.P.A. N° 13 CASA DE LA CULTURA(DOC:BERSANO), 140170100-ESCUELA HIPOLITO BUCHARDO, 140173100-J.DE INF. HIPOLITO BUCHARDO
1402960 UNION   SAN ANTONIO DE LITIN    Estatal Provincial  DGEA, DGEI, DGETyFP 3029, TZONAXI, Z11  SAN ANTONIO DE LITIN    3601300101020009    360102097366    0250347         SI / SI -32.212126;-62.635999 / -32.2122558;-62.6360432 / -32.2131931096409;-62.6291815804363   Rural Aglomerado    140049401, 140313000, 140313300, 140483400, 140499800   140049401-C.E.N.M.A. N° 201 ANEXO SAN ANTONIO DE LITIN, 140313000-I.P.E.A. Nº 214. MANUEL BELGRANO, 140313300-J.DE INF. PABLO A. PIZZURNO, 140483400-C.E.N.P.A. DE SAN ANTONIO DE LITIN, 140499800-C.E.N.P.A. B DE SAN ANTONIO DE LITIN
awk text-processing
  • 5 5 respostas
  • 636 Views

5 respostas

  • Voted
  1. Best Answer
    steeldriver
    2022-04-28T16:21:35+08:002022-04-28T16:21:35+08:00

    Você pode fazer isso awkdividindo a coluna composta ,e fazendo um loop sobre o resultado:

    awk -F'\t' 'BEGIN{OFS=FS} {n=split($2,a,/,/); for(i=1;i<=n;i++){$2 = a[i]; print}}' file
    

    Talvez de forma mais clara, você poderia fazer isso com Miller - em particular, usando o verbo nest :

    $ cat file
    C1      C2      C3
    a       b,c     d
    e       f,g,h   i
    j       k       l
    
    $ mlr --tsv nest --explode --values --across-records --nested-fs ',' -f C2 file
    C1      C2      C3
    a       b       d
    a       c       d
    e       f       i
    e       g       i
    e       h       i
    j       k       l
    

    Mais compactamente --explode --values --across-records --nested-fs ','pode ser substituído por--evar ','

    • 10
  2. Philippos
    2022-04-29T00:05:42+08:002022-04-29T00:05:42+08:00

    Como você também marcou a pergunta com sed, sinto-me convidado a adicionar uma sedsolução:

    sed -e '/,/{s//\n/;h;s/[^\t]*\n//;x;s/\n[^\t]*//p;G;D;}'
    

    (Nota: Para legibilidade eu usei \npara nova linha e \tpara tab como você pode fazer com GNU sed. Para uma solução portátil, use uma barra invertida com uma nova linha real em vez de \ne uma tabulação real para \t, inserido ctrlVseguido por tab)

    As linhas com uma vírgula são copiadas para o espaço de espera, uma cópia é impressa com o que está antes da vírgula, a outra cópia vai para o próximo ciclo com a parte após a vírgula. Em detalhe:

    • Para evitar confusão com várias vírgulas, substituímos uma por uma nova linhas//\n/
    • salve uma cópia no hespaço antigo antes de estragar a linha
    • s/[^\t]*\n//remove a parte até a primeira vírgula
    • então nós xmudamos os buffers
    • s/\n[^\t]*//premove a parte a partir da vírgula e a imprime
    • Gacrescenta o espaço de espera ao espaço de padrão. Isso pode conter vírgulas de adição, então
    • Dremove a primeira linha (já impressa) e recomeça com o resto da linha
    • 4
  3. dave_thompson_085
    2022-04-29T20:04:12+08:002022-04-29T20:04:12+08:00

    awk(ou perlno awkmodo) é provavelmente a melhor solução padrão, mas você pode fazer isso de forma razoavelmente eficiente na maioria dos shells, especialmente aqueles com arrays ( ksh, bash, zsh):

    set -f # split but don't glob unquoted substitutions
    #bash
    while IFS=$'\t' read -ra ary; do 
    #ksh
    while read -r line; do IFS=$'\t'; ary=($line)
    #zsh I haven't worked out
    
      IFS=,; for v in ${ary[17]}; do 
        ary[17]=$v; IFS=$'\t'; printf '%s\n' "${ary[*]}"
      done
      # bash,ksh arrays are 0-origin versus 1-origin fields in awk
      # we don't need to special-case no-comma, it splits to a single value
    done <input >output
    

    Para shells antigos/limitados sem arrays, use os parâmetros posicionais como (pode variar):

    set -f
    while read -r line; do IFS=$'\t'; set -- $line
      IFS=,; for v in ${18}; do
        # can't alter $num so yucky
        for i in $(seq $#); do
          case $i in (1);; (*) printf '\t';; esac
          case $i in (18) printf %s "$v";; (*) eval printf %s \"\${$i}\";; esac
        done
        # or maybe i=1; while [ $i -le $# ]; do ... i=$((i+1)); done
        # where [/test is likely shell builtin and seq is unlikely 
      done
    done <input >output
    
    • 2
  4. guest_7
    2022-05-01T00:59:40+08:002022-05-01T00:59:40+08:00

    Usando perl

    ## Column of Interest
    CoI=2 
    perl -sF'\t' -aple '$"="\t";
      $_ = join $\, map { $F[$I]=$_;"@F" } split /,/, $F[$I]
    ' -- -I="$((CoI-1))" file
    
    • perlopções usadas:-

      • -p isso faz com que o Perl leia um arquivo linha por linha e produza a linha antes do próximo ciclo.
      • -a liga o modo de divisão automática, em que o registro de entrada ($_) é dividido e os componentes colocados no array (@F)
      • -l faz com que o separador de registro de entrada e o separador de registro de saída sejam novas linhas.
      • -s liga o processamento rudimentar do switch. Com sua ajuda, definimos uma variável global $I na linha de comando.
      • -F separador de campo especificado aqui
      • -e Código Perl especificado aqui.
    • perlvariáveis ​​internas usadas: -

      • $_ registro atual sendo processado.
      • $" junção de elementos de matriz
      • @F campos de divisão de registro atual armazenados nesta matriz. É indexado a zero
      • $\ separador de registro de saída
    • Código Perl:-

      • divida a coluna de interesse $F[$I] em torno de vírgula e atribua progressivamente os elementos de divisão à coluna se interesse e junte a matriz @F com a variável $" e atribua ao registro de entrada ($_)
      • a ação padrão é imprimir automaticamente o registro de entrada.

    CoI=2
    awk -F '\t' -v coi="$CoI" '
    BEGIN { OFS=FS;s[1]=ORS }
    NF >= coi {
      split($(coi),a,",")
      for (i=t=""; ++i in a;) {
        $(coi) = a[i]
        t = t s[i>1] $0
      }
      $0=t
    }1
    ' file
    

    Usando GNU sed no modo regex estendido (-E):

    CoI=2
    sed -E '
      s/[^\t]+/\n&\n/'"$CoI"'
      s/(\n.*)(\n.*)/\2\1,/
      :loop
        s/\n(.*\n)([^,]+),/\2\1/
        P;/\n$/d
        s/[^\t]+/\n/'"$CoI"'
      tloop
    ' file
    

    Esta é uma operação de progresso em um registro do que o sed está fazendo:

       pat_spc       output
     a b,c,d e         -
     a \nb,c,d\n e     -
     a \n e\nb,c,d,    -
     a b e\nc,d,      a b e
     a \n e\c,d,        -
     a c e\nd,        a c e
     a \n e\nd,         -
     a d e\n          a d e
    /\n$/ stop, fetch next line
    

    CoI=2
    python3 -c 'import sys
    
    ifile,coi = sys.argv[1:]
    coi = int(coi)-1
    fs,rs,ofs,ors = ("\t","\n") * 2
    
    with open(ifile) as f:
      for l in f:
        F = l.rstrip(rs).split(fs)
        for e in F[coi].split(","):
          F[coi] = e
          print(*F,sep=ofs)
    ' file "$CoI"
    

    com bourne shell embutido usando array de parâmetros posicionais

    cleanup() {
      echo cleaning up temp files... >&2
      /bin/rm -f -- "$temp"
    }
    trap cleanup EXIT
    set -u
    
    #-------------------+
    # user input section
    #-------------------+
    CoI=2
    inp='inmuebles.txt'
    #-------------------+
    
    : <<\_README_
    1. CoI standing for column of interest.
    2. CoT must be a positive integer.
    3. CoI must not be more than the number of fields in the unput.
    4. inp stores the input file name, 
    possibly with full or relative path, to make it accessible to the script.
    5. All lines must have same number of fields.
    6. No field to have TAB and/or newline.
    7. Field separator is TAB.
    8. File should be readable by the user and be a regular ascii text file.
    _README_
    
    IFS=$(printf '\t')
    temp=$(mktemp) del=
    
    while IFS= read -r line <&3
    do
      set -f;set -- $line;set +f
    
      : ${del:=$(dc <<eof
    1 $# $CoI-+n
    eof
    )}
    
      while case $# in "$del") break;; esac
      do
        printf '%s\t' "$1"
        shift
      done > "$temp"
    
      x1=$1;shift
    
      for csv in $(set -f;IFS=',';set -- $x1;printf '%s\t' "$@")
      do
        printf '%s' "$(cat < "$temp")" "$csv"
        case $# in 0) echo; break;; esac
        printf '\t%s\n' "$*"
      done
    done 3< "$inp"
    

    • 1
  5. Praveen Kumar BS
    2022-04-28T23:22:31+08:002022-04-28T23:22:31+08:00
     while read line
     do
     fic=$(echo $line | awk '{print $1}')
     laco=$(echo $line | awk '{print $NF}')
     secon_colu=$(echo $line| awk '$2 ~ /,/{print $2}')
     if [[ "$secon_colu" =~ "," ]]
     then
     for ko in $(echo $line | awk '$2 ~ /,/{print $2}'| sed 's/,/ /g')
     do
     echo "$fic $ko  $laco"
     done
     else
     echo $line
     fi
     done<file.txt
    

    resultado

    C1 C2 C3
    a b  d
    a c  d
    e f  i
    e g  i
    e h  i
    j k l
    
    • -2

relate perguntas

  • Reorganize as letras e compare duas palavras

  • Subtraindo a mesma coluna entre duas linhas no awk

  • Embaralhamento de arquivo de várias linhas

  • como posso alterar o caso do caractere (de baixo para cima e vice-versa)? ao mesmo tempo [duplicado]

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve