Eu tenho um arquivo csv muito grande. Como você removeria o último ,
com sed (ou similar)?
...
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0],
]
Saída desejada
...
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]
O seguinte comando sed excluirá a última ocorrência por linha, mas eu quero por arquivo.
sed -e 's/,$//' foo.csv
Nem isso funciona
sed '$s/,//' foo.csv
Responder1
Usandoawk
Se a vírgula estiver sempre no final da penúltima linha:
$ awk 'NR>2{print a;} {a=b; b=$0} END{sub(/,$/, "", a); print a;print b;}' input
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]
Usando awk
ebash
$ awk -v "line=$(($(wc -l <input)-1))" 'NR==line{sub(/,$/, "")} 1' input
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]
Usandosed
$ sed 'x;${s/,$//;p;x;};1d' input
[11911,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11912,0,"BUILDER","2014-10-15","BUILDER",0,0],
[11913,0,"BUILDER","2014-10-15","BUILDER",0,0]
]
Para OSX e outras plataformas BSD, tente:
sed -e x -e '$ {s/,$//;p;x;}' -e 1d input
Usandobash
while IFS= read -r line
do
[ "$a" ] && printf "%s\n" "$a"
a=$b
b=$line
done <input
printf "%s\n" "${a%,}"
printf "%s\n" "$b"
Responder2
Simplesmente você pode tentar o comando Perl one-liner abaixo.
perl -00pe 's/,(?!.*,)//s' file
Explicação:
,
Corresponde a uma vírgula.(?!.*,)
A antecipação negativa afirma que não haveria uma vírgula após a vírgula correspondente. Portanto, corresponderia à última vírgula.s
E o mais importante és
o modificador DOTALL, que faz com que o ponto corresponda até mesmo aos caracteres de nova linha.
Responder3
lcomma() { sed '
$x;$G;/\(.*\),/!H;//!{$!d
}; $!x;$s//\1/;s/^\n//'
}
Isso deve remover apenas a última ocorrência de a ,
em qualquer arquivo de entrada - e ainda imprimirá aquelas em que a ,
não ocorre. Basicamente, ele armazena em buffer sequências de linhas que não contêm vírgula.
Quando encontra uma vírgula ele troca o buffer de linha atual pelo buffer de espera e dessa forma imprime simultaneamente todas as linhas que ocorreram desde a última vírgulaelibera seu buffer de retenção.
Eu estava vasculhando meu arquivo de histórico e encontrei isto:
lmatch(){ set "USAGE:\
lmatch /BRE [-(((s|-sub) BRE)|(r|-ref)) REPL [-(f|-flag) FLAG]*]*
" "${1%"${1#?}"}" "$@"
eval "${ZSH_VERSION:+emulate sh}"; eval '
sed " 1x; \\$3$2!{1!H;\$!d
}; \\$3$2{x;1!p;\$!d;x
}; \\$3$2!x;\\$3$2!b'"
$( unset h;i=3 p=:-:shfr e='\033[' m=$(($#+1)) f=OPTERR
[ -t 2 ] && f=$e\2K$e'1;41;17m}\r${h-'$f$e\0m
f='\${$m?"\"${h-'$f':\t\${$i$e\n}\$1\""}\\c' e=} _o=
o(){ IFS=\ ;getopts $p a "$1" &&
[ -n "${a#[?:]}" ] &&
o=${a#-}${OPTARG-${1#-?}} ||
! eval "o=$f;o=\${o%%*\{$m\}*}"
}; a(){ case ${a#[!-]}$o in (?|-*) a=;;esac; o=
set $* "${3-$2$}{$((i+=!${#a}))${a:+#-?}}"\
${3+$2 "{$((i+=1))$e"} $2
IFS=$; _o=${_o%"${3+$_o} "*}$*\
}; while eval "o \"\${$((i+=(OPTIND=1)))}\""
do case ${o#[!$a]} in
(s*|ub) a s 2 '' ;;
(r*|ef) a s 2 ;;
(f*|lag) a ;;
(h*|elp) h= o; break ;;
esac; done; set -f; printf "\t%b\n\t" $o $_o
)\"";}
Na verdade, é muito bom. Sim, ele usa eval
, mas nunca passa nada além de uma referência numérica aos seus argumentos. Ele cria sed
scripts arbitrários para lidar com a última correspondência. Eu vou te mostrar:
printf "%d\" %d' %d\" %d'\n" $(seq 5 5 200) |
tee /dev/fd/2 |
lmatch d^.0 \ #all re's delimit w/ d now
-r '&&&&' \ #-r or --ref like: '...s//$ref/...'
--sub \' sq \ #-s or --sub like: '...s/$arg1/$arg2/...'
--flag 4 \ #-f or --flag appended to last -r or -s
-s\" \\dq \ #short opts can be '-s $arg1 $arg2' or '-r$arg1'
-fg #tacked on so: '...s/"/dq/g...'
Isso imprime o seguinte em stderr. Esta é uma cópia da lmatch
entrada de:
5" 10' 15" 20'
25" 30' 35" 40'
45" 50' 55" 60'
65" 70' 75" 80'
85" 90' 95" 100'
105" 110' 115" 120'
125" 130' 135" 140'
145" 150' 155" 160'
165" 170' 175" 180'
185" 190' 195" 200'
O subshell ed da função eval
itera todos os seus argumentos uma vez. À medida que passa por eles, ele itera um contador apropriadamente, dependendo do contexto de cada opção, e pula tantos argumentos para a próxima iteração. A partir de então, ele faz uma de algumas coisas por argumento:
- Para cada opção, o analisador de opções
$a
adiciona$o
.$a
é atribuído com base no valor$i
que é incrementado pela contagem de argumentos para cada argumento processado.$a
é atribuído um dos dois valores a seguir:a=$((i+=1))
- isto é atribuído se uma opção curta não tiver seu argumento anexado a ela ou se a opção for longa.a=$i#-?
- isto é atribuído se a opção for curta efaztenha seu argumento anexado a ele.a=\${$a}${1:+$d\${$(($1))\}}
- Independentemente da atribuição inicial,$a
o valor de é sempre colocado entre colchetes e - em um-s
caso - às vezes$i
é incrementado mais um e o campo delimitado adicional é anexado.
O resultado é que eval
nunca é passada uma string contendo incógnitas. Cada um dos argumentos da linha de comando é referido pelo seu número de argumento numérico - até mesmo o delimitador que é extraído do primeiro caractere do primeiro argumento e é o único momento em que você deve usar qualquer caractere sem escape. Basicamente, a função é um gerador de macro - ela nunca interpreta os valores dos argumentos de nenhuma maneira especial porque sed
pode(e irá, claro)lidar facilmente com isso ao analisar o script. Em vez disso, ele apenas organiza seus argumentos de maneira sensata em um script viável.
Aqui estão alguns resultados de depuração da função em funcionamento:
... sed " 1x;\\$2$1!{1!H;\$!d
}; \\$2$1{x;1!p;\$!d;x
}; \\$2$1!x;\\$2$1!b
s$1$1${4}$1
s$1${6}$1${7}$1${9}
s$1${10#-?}$1${11}$1${12#-?}
"
++ sed ' 1x;\d^.0d!{1!H;$!d
}; \d^.0d{x;1!p;$!d;x
}; \d^.0d!x;\d^.0d!b
sdd&&&&d
sd'\''dsqd4
sd"d\dqdg
'
E assim lmatch
pode ser usado para aplicar facilmente expressões regulares aos dados após a última correspondência em um arquivo. O resultado do comando que executei acima é:
5" 10' 15" 20'
25" 30' 35" 40'
45" 50' 55" 60'
65" 70' 75" 80'
85" 90' 95" 100'
101010105dq 110' 115dq 120'
125dq 130' 135dq 140sq
145dq 150' 155dq 160'
165dq 170' 175dq 180'
185dq 190' 195dq 200'
...que, dado o subconjunto da entrada do arquivo que segue a última vez /^.0/
que foi correspondido, aplica as seguintes substituições:
sdd&&&&d
- substitui$match
-se 4 vezes.sd'dsqd4
- a quarta aspa simples após o início da linha desde a última partida.sd"d\dqd2
- idem, mas para aspas duplas e globalmente.
E assim, para demonstrar como se pode usar lmatch
para remover a última vírgula de um arquivo:
printf "%d, %d %d, %d\n" $(seq 5 5 100) |
lmatch '/\(.*\),' -r\\1
SAÍDA:
5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100
Responder4
verhttps://stackoverflow.com/questions/12390134/remove-comma-from-last-line
Isso funcionou para mim:
$cat input.txt
{"name": "secondary_ua","type":"STRING"},
{"name": "request_ip","type":"STRING"},
{"name": "cb","type":"STRING"},
$ sed '$s/,$//' < input.txt >output.txt
$cat output.txt
{"name": "secondary_ua","type":"STRING"},
{"name": "request_ip","type":"STRING"},
{"name": "cb","type":"STRING"}
Minha melhor maneira é remover a última linha e após remover a vírgula, adicionar o caractere] novamente