Excluir coluna que inclui um texto específico

6

Eu estava procurando uma opção para excluir uma coluna com um texto específico, como

"ddd"

aaa bbb ccc ddd eee fff
1   2   3   4   5   6
2   3   4   5   6   0

Para que a saída pareça assim:

aaa bbb ccc eee fff
1   2   3   5   6
2   3   4   6   0

Eu sei que existe a opção fácil de excluir a coluna 4 e que faz o mesmo trabalho, mas meus arquivos * .csv não estão classificados. Alguma idéia?

    
por ItsSomethng 20.09.2015 / 01:54

4 respostas

8

sed não é a ferramenta certa para isso. Experimente awk :

$ awk -v OFS='\t' 'NR==1{for (i=1;i<=NF;i++)if ($i=="ddd"){n=i-1;m=NF-(i==NF)}} {for(i=1;i<=NF;i+=1+(i==n))printf "%s%s",$i,i==m?ORS:OFS}' file
aaa     bbb     ccc     eee     fff
1       2       3       5       6
2       3       4       6       0

Isso pressupõe que a string a ser removida, ddd nesse caso, aparece como um campo na primeira linha.

Como funciona

  • -v OFS='\t'

    Isso define o separador de campo de saída como uma guia. Se você estiver usando outra coisa, mude isso.

  • NR==1{for (i=1;i<=NF;i++)if ($i=="ddd"){n=i-1;m=NF-(i==NF)}}

    Isso varre todas as colunas na primeira linha. Nós salvamos o número da coluna com ddd (menos um) na variável n .

    Ele também define m para o número da última coluna, exceto no caso de i ser a última coluna, caso em que ela é definida como NF-1 .

  • for(i=1;i<=NF;i+=1+(i==n))printf "%s%s",$i,i==m?ORS:OFS

    Isso imprime cada campo, pulando o campo em que ddd apareceu na primeira linha.

    i+=1 aumentaria i em um em cada loop. i+=1+(i==n) incrementa i em um em cada loop, exceto se i==n , nesse caso, i é incrementado em 2. Isso tem o efeito de pular a coluna da direita.

    printf "%s%s",$i,i==m?ORS:OFS imprime coluna i seguido por um separador de coluna, OFS ou um separador de linha, ORS , dependendo se i é a última coluna.

Várias linhas

Para aqueles que preferem comandos como este, escritos em várias linhas:

awk -v OFS='\t' '

NR==1{
    for (i=1;i<=NF;i++)
        if ($i=="ddd") {
            n=i-1
            m=NF-(i==NF)
        }
    }

{
    for(i=1;i<=NF;i+=1+(i==n))
        printf "%s%s",$i,i==m?ORS:OFS
}

' file

Usando arquivos separados por vírgulas

Se quisermos que a entrada e a saída sejam separadas por vírgula, precisamos alterar tanto o separador de campo de entrada (com -F ) quanto o separador de campo de saída. Por exemplo, considere este arquivo de entrada:

$ cat file2
aaa,bbb,ccc,ddd,eee,fff
1,2,3,4,5,6
2,3,4,5,6,0

Em seguida, use:

$ awk -F, -v OFS=, 'NR==1{for (i=1;i<=NF;i++)if ($i=="ddd"){n=i-1;m=NF-(i==NF)}} {for(i=1;i<=NF;i+=1+(i==n))printf "%s%s",$i,i==m?ORS:OFS}' file2
aaa,bbb,ccc,eee,fff
1,2,3,5,6
2,3,4,6,0
    
por 20.09.2015 / 02:09
5

Isso pode ser possível em sed (a maioria das coisas é), mas duvido que seja mais simples do que usar outra ferramenta. A abordagem mais fácil é primeiro obter o número do campo desejado e, em seguida, passar pelo arquivo imprimindo o restante. Por exemplo, em Perl:

$ perl -lane 'if($.==1){for(0..$#F){$d=$_ if $F[$_] eq "ddd"}} 
              print "@F[0..$d-1] @F[$d+1..$#F]"' file
aaa bbb ccc eee fff
1 2 3 5 6
2 3 4 6 0

No entanto, isso estraga a formatação. Se isso for importante, use a resposta de John1024 .

    
por 20.09.2015 / 02:27
3
#!/bin/bash
read -a header <file                 # read first line into array "header"
for i in ${!header[@]}               # iterate through array indexes
do
    if [ "${header[i]}" = "ddd" ]    # find column equal the pattern
    then
        n=$[++i]                     # put column number in variable "n"
        break
    fi
done
# print all column except found delimited by <TAB>
cut --complement -f ${n} < <(tr -s ' ' '\t' <6)

Mas se você gosta pode ser feito "apenas por sed " (mesmo que não seja o ideal)

sed "s/\S\+\s*//$(sed 's/\s\+/\n/g;1q' file | sed -n '/ddd/=')" file

\S\+\s* - Coluna média 1 (símbolo (s) não espacial (es) seguido de alguns espaços possíveis). s/<pattern>//<number> - Substitua apenas número a ocorrência de padrão .
s/\s\+/\n/g - Substitua cada espaço ou bloco de espaços por \n ewline apenas para% 1q - primeira linha (cabeçalho). Então, transforma a coluna do cabeçalho em linhas.
sed -n '/ddd/= - imprime o número da linha onde padrão existe == No. da coluna

    
por 20.09.2015 / 10:01
0
awk -F "\t" -v "Pat=ddd" 'NR == 1 {for( i = 1; i <= NF; i++) Take[ i] = (Pat != $i)} {for ( i = 1; i <= NF; i++) if( Take[ i]) printf $i FS; print ""}' YourFile
  • campo de impressão que não está excluído
  • use o mesmo separador como entrada ( -F "\t" )
  • use a variável para seleção de padrões para excluir ( -v "Pat=ddd" ). poderia ser facilmente modificado para seleção de regex se muitas opções para fazer
  • feito com 2 loop para evitar confusão, mas pode ser feito em apenas um
por 21.09.2015 / 10:29