Como posso fazer o seguinte em um arquivo CSV usando sedou awk?
- Excluir uma coluna
- Duplicar uma coluna
- Mover uma coluna
Eu tenho uma mesa grande com mais de 200 linhas e não estou familiarizada com isso sed.
Como posso fazer o seguinte em um arquivo CSV usando sedou awk?
Eu tenho uma mesa grande com mais de 200 linhas e não estou familiarizada com isso sed.
Respostas:
Além de como cortar e reorganizar os campos (abordados nas outras respostas), há a questão dos campos CSV peculiares.
Se seus dados se enquadram nessa categoria "peculiar", um pouco de pré e pós- filtragem podem cuidar disso. Os filtros mostrados abaixo exigem os personagens \x01, \x02, \x03, \x04para não aparecer em qualquer lugar em seus dados.
Aqui estão os filtros envolvidos em um awkdespejo de campo simples .
Nota: o campo cinco possui um layout de "campo entre aspas" inválido / incompleto, mas é benigno no final de uma linha (dependendo do analisador CSV). Mas, é claro, causaria resultados imprevisíveis e problemáticos se fosse trocada de sua posição atual de fim de linha .
Atualizar; user121196 apontou um erro quando uma vírgula precede uma citação à direita. Aqui está a correção.
Os dados
cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF
O código
sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g'
A saída:
field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five
"15111 N. Hayden Rd., Ste 160,"
""
Aqui está o pré filtro , expandido com comentários.
O filtro de postagem é apenas uma reversão de \x01. \x02, \x03,\x04
sed -r '
s/^/,/ # add a leading comma delimiter
s/\\"/\x01/g # obfuscate escaped quotation-mark (\")
s/,"([^"]*)"/,\x02\1\x03/g # obfuscate quotation-marks
s/,"/,\x02/ # when no trailing quote on last field
:MC # obfuscate commas embedded in quotes
s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
tMC
s/^,// # remove spurious leading delimiter
'
Isso depende se o seu arquivo CSV usa vírgulas apenas para delimitadores ou se você tem loucura como:
campo um, "campo dois", campo três
Isso pressupõe que você esteja usando um arquivo CSV simples:
Você pode se livrar de uma única coluna de várias maneiras; Eu usei a coluna 2 como exemplo. A maneira mais fácil é provavelmente usar cut, o que permite especificar um delimitador -de quais campos você deseja imprimir -f; isto diz para ele dividir em vírgulas e campo de saída 1 e campos 3 até o final:
$ cut -d, -f1,3- /path/to/your/file
Se você realmente precisar usar sed, você pode escrever uma expressão regular que corresponda aos primeiros n-1campos, o nth e o restante, e pular a saída do nth (aqui né 2, então o primeiro grupo corresponde ao 1tempo :) \{1\}:
$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file
Existem várias maneiras de fazer isso awk, nenhuma delas particularmente elegante. Você pode usar um forloop, mas lidar com a vírgula à direita é uma dor; ignorando que seria algo como:
$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file
Acho mais fácil substrgerar o campo 1 e depois usá-lo para extrair tudo após o campo 2:
$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file
Isso é irritante para colunas mais adiante
Em sedesta é essencialmente a mesma expressão como antes, mas você também capturar a coluna de destino e incluir esse grupo várias vezes na substituição:
$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file
No awkcaminho do loop for, seria algo como (novamente ignorando a vírgula à direita):
$ awk -F, '{
for(i=1; i<=NF; i++) {
if(i == 2) printf "%s,", $i;
printf "%s,", $i
}
print NL
}' /path/to/your/file
O substrcaminho:
$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file
(tcdyl apresentou um método melhor em sua resposta )
Eu acho que a sedsolução segue naturalmente as outras, mas começa a ficar ridiculamente longa
awké a sua melhor aposta. awkimprime campos por número, então ...
awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file
Para remover uma coluna, não a imprima:
awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file
Para alterar a ordem:
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file
Redirecionar para um arquivo de saída.
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file
awk também pode formatar a saída.
Dado um arquivo delimitado por espaço no seguinte formato:
1 2 3 4 5
Você pode remover o campo 2 com o awk da seguinte forma:
awk '{ sub($2,""); print}' file
que retorna
1 3 4 5
Substitua a coluna 2 pela coluna n, onde apropriado.
Para duplicar a coluna 2,
awk '{ col = $2 " " $2; $2 = col; print }' file
que retorna
1 2 2 3 4 5
Para alternar as colunas 2 e 3,
awk '{temp = $2; $2 = $3; $3 = temp; print}'
que retorna
1 3 2 4 5
O awk geralmente é muito bom em lidar com o conceito de campos . Se você está lidando com um CSV, e não com um arquivo delimitado por espaço, pode simplesmente usar
awk -F,
para definir seu campo como vírgula, em vez de um espaço (que é o padrão). Existem vários recursos on-line bons do awk, um dos quais eu listo como fonte abaixo.
Fonte para # 3
awk, mas parece saída de espaço-separada, mesmo que o separador de campo é ,(o campo-separadores apenas controla como ele lida com a entrada)
Isso funcionará para excluir
awk '{$2="";$0=$0;$1=$1}1'
Entrada
a b c d
Saída
a c d