Reorganizando as colunas usando o awk

12

Estou tentando mover a 7ª coluna do meu arquivo csv para o final usando

awk -F '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}',OFS= "$file"

onde $ file é um arquivo .csv em um diretório. No entanto, a saída é

awk:                          ^ syntax error

Alguém sabe como corrigir esse erro?

rmb
fonte
7
Ao mostrar erros do awk, você precisa mostrar a coisa toda. A ^indica a parte específica do comando onde o erro foi encontrado.
terdon

Respostas:

10

A -Fopção precisa de um argumento: -F,por exemplo.

O final do awkscript deve ser separado por um (caractere de espaço) com o restante dos parâmetros.

Se o separador de campos for ,e você deseja mantê-lo, e se o número de colunas for constante e menor que ou igual a 11, tente isso:

awk -F, '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' OFS=, "$file"
Jay jargot
fonte
8
@anuribs muito poucos programas permitem isso. O caminho padrão é command file > newfile && mv newfile file. Dito isto, a versão mais recente do GNU awkpara apoiar este: gawk -i inplace '{blah blah}' file.
terdon
1
Como alternativa, em vez de mv newfile filevocê pode usar cat newfile > file ; rm -f newfile- isso preserva o inode e as permissões de file.
cas
e geralmente é uma boa idéia usar, em mktempvez de codificar, nomes de arquivos temporários em scripts. por exemplotf=$(mktemp) ; command file > "$tf" ; cat "$tf" > file ; rm -f "$tf"
cas 06/07
7

Solução mais curta seria

awk -F',+' -v OFS=, '{$(NF+1)=$7; $7=""; $0=$0; $1=$1}1' file

Não tenho certeza se ,+funcionará em todas as awkversões, mas funciona pelo menos no GNU awk, também com o -cmodo de compatibilidade.

Explicação:

  • $(NF+1)=$7: primeiro, adicionamos o sétimo campo ao final da linha (pode ser $12=$7neste caso)
  • $7="": na próxima etapa, o 7º campo é apagado (mas os delimitadores circundantes permanecem)
  • para remover delimitadores, precisamos redefinir o registro inteiro (via $0=$0) tratando várias vírgulas como separador de campo (isso é feito via -F',+', aqui +significa uma ou mais vezes) e também reorganizar o registro atual via $1=$1para forçar a reconstrução da linha usando o campo de saída definido anteriormente separador (definido por uma opção -v OFS=,)
  • depois de todo o embaralhamento, estamos prontos para imprimir o resultado com 1

Exemplo de entrada:

1,2,3,4,5,6,7,8,9,10,11

resultado

1,2,3,4,5,6,8,9,10,11,7
jimmij
fonte
E se outras colunas estiverem em branco? Mas, sim, FS é uma expressão regular no POSIX (se houver vários caracteres), portanto, ,+deve funcionar.
precisa saber é o seguinte
(1) Entendo que fazer a sétima coluna de dados de entrada “desaparecer”, e não apenas configurá-lo como nulo, é uma parte complicada desse problema. Mas, como diz Random832, sua solução obstrui colunas em branco (por exemplo, all,ball,call,,,fallall,ball,call,fall). (2)  $(NF+1)=$7é uma abordagem inteligente. IMHO, $0 = $0 OFS $7é um pouco mais claro, apenas alguns caracteres a mais, e parece fazer a mesma coisa. Você consegue pensar em uma situação em que $0 = $0 OFS $7não faça o mesmo que o seu código?
G-Man diz 'Reinstate Monica'
@ Random832 @ G-Man sim, alguns casos extremos como campos em branco, linhas em branco ou NF <7 devem ser tratados separadamente ou deve-se reorganizar o código. Esta é apenas uma idéia, não uma "solução completa" para todos os casos gerais, que deve ficar clara. $0=$0 OFS $7provavelmente é idêntico a $(NF+1)=$7, mas apenas com o restante do código inalterado, não em geral.
jimmij
5

Se estiver imprimindo com OFS=, portanto, sem separador entre os campos, você pode simplesmente salvar o valor de $7em uma variável, definido $7como vazio e imprimir a linha e a variável diretamente. Você não precisa especificar todos os campos:

$ cat file
1,2,3,4,5,6,7,8
$ awk -F, -vOFS= '{k=$7; $7=""; print $0,k}' file 
12345687
terdon
fonte
3

Você provavelmente quer dizer:

awk -F, -v OFS='' '{print $1,$2,$3,$4,$5,$6,$8,$9,$10,$11,$7}' "$file"
Michael Vehrs
fonte
Você sabe que awknunca vê as aspas simples OFS='', certo? Você também pode digitar OFS=; é exatamente o mesmo.
Curinga
1
Sim, eu percebo isso. No entanto, não gosto de tarefas pendentes.
Michael Vehrs
3

Você não diz especificamente que você queria usar awk, e você me disse que queria usar a edição no local, como fornecido pelo sed -i, então aqui é uma sed -ivariante. Geralmente awké melhor trabalhar com colunas, mas esse é um caso em que eu prefiro sed, porque naturalmente lida com números arbitrários de colunas.

MOVECOL=7
N=$((MOVECOL-1))
sed -r -e "s/^(([^,]*,){$N})([^,]*),(.*)/\1\4,\3/" -i test.csv

Explicação:

  • -r seleciona regexps estendidos, para evitar muitas barras invertidas
  • o primeiro grupo é $ N repetições de cadeias terminadas por vírgula; em outras palavras, as colunas antes da que queremos mover, com uma vírgula final
  • segundo grupo é a repetição de $ N-ésimo, esquecemos
  • terceiro grupo é a coluna que queremos mover, sem a vírgula final
  • O quarto grupo é composto por todas as colunas após a que queremos mover, sem vírgula antes
  • substituímos por primeiro grupo, último grupo e pela coluna que extraímos, inserindo a vírgula conforme necessário.

É claro que isso não funcionará com arquivos que ocultam vírgulas entre aspas (ou pior, escapam), mas o awk também não lidará com isso sem algumas acrobacias sérias. Se você tiver esse problema, seria melhor usar o perlmódulo Text:CSVou o pythonmódulo csv.

Lei29
fonte
2

Algumas awkvariantes (supondo que seu arquivo esteja dentro da variável $file)

  • Aqui você pode pedalar por toda a cor, imprimir com o separador de campos (OFS) e imprimir o terminador de registro (ORS) no final da linha.

    awk  -F',' -v OFS=,                                \
    '{for(i=1;i<=NF;i++) if (i!=7) printf "%s",$i OFS; \
    printf "%s",$7;printf ORS}' "$file"
  • Aqui com o uso de um regex e a gensub()função

    gawk -F',+' -v OFS=, '{$0=gensub(/\s*\S+/,"",7) OFS $7}1' "$file"

    matando o campo e imprimindo no final da linha.

    • $0 é todo o registro
    • $né o nono registro
    • NF é o número de campos da linha atual
    • OFS o separador arquivado de saída
    • ORS o terminador do registro de saída
    • 1é o truque a ser dito para ativar truee imprimir o padrão ( $0).

Atualizar ...

Quase esqueço que é possível mudar todas as colunas após a .

awk  -F',' -v OFS=, '{tmp=$7; for(i=7;i<=NF;i++) $i=$(i+1); $NF=tmp}1 ' "$file"
Hastur
fonte
(1) Indiscutivelmente, OFS $7seria mais robusto que "," $7. (2) Eu acredito que ", " $7é errado, na medida em que a questão indica que o OP não quer espaços após as vírgulas. (E, se os dados de entrada tivessem espaços após as vírgulas, $7eles já começariam com um espaço e você incluiria um adicional.)
G-Man diz 'Reinstate Monica'
@ G-Man Era principalmente para propor algumas idéias, algumas variantes. Obrigado, para o primeiro lugar, concordo sobre OFS $7, não só mais robusta, mas ainda mais geral ( "pressa deixa resíduos" )
Hastur