Ich habe eine sehr große CSV-Datei. Wie würden Sie das Allerletzte ,
mit sed (oder ähnlich) entfernen?
...
[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],
]
Gewünschte Ausgabe
...
[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]
]
Der folgende Sed-Befehl löscht das letzte Vorkommen pro Zeile, aber ich möchte es pro Datei.
sed -e 's/,$//' foo.csv
Das funktioniert auch nicht
sed '$s/,//' foo.csv
Antwort1
Verwenden vonawk
Wenn das Komma immer am Ende der vorletzten Zeile steht:
$ 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]
]
Verwenden awk
undbash
$ 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]
]
Verwenden vonsed
$ 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]
]
Versuchen Sie für OSX und andere BSD-Plattformen:
sed -e x -e '$ {s/,$//;p;x;}' -e 1d input
Verwenden vonbash
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"
Antwort2
Sie können einfach den folgenden Perl-Einzeilerbefehl ausprobieren.
perl -00pe 's/,(?!.*,)//s' file
Erläuterung:
,
Entspricht einem Komma.(?!.*,)
Negativer Lookahead bedeutet, dass nach dem übereinstimmenden Komma kein Komma folgt. Es würde also mit dem letzten Komma übereinstimmen.s
Und das Wichtigste ists
der DOTALL-Modifikator, der dafür sorgt, dass der Punkt auch mit Zeilenumbruchzeichen übereinstimmt.
Antwort3
lcomma() { sed '
$x;$G;/\(.*\),/!H;//!{$!d
}; $!x;$s//\1/;s/^\n//'
}
Dadurch sollte nur das letzte Vorkommen von a ,
in jeder Eingabedatei entfernt werden - und es werden trotzdem die Dateien gedruckt, in denen a ,
nicht vorkommt. Im Grunde werden damit Zeilenfolgen gepuffert, die kein Komma enthalten.
Wenn es auf ein Komma stößt, tauscht es den aktuellen Zeilenpuffer mit dem Haltepuffer aus und druckt auf diese Weise gleichzeitig alle Zeilen aus, die seit dem letzten Komma aufgetreten sind.Undgibt seinen Haltepuffer frei.
Ich habe gerade in meiner Verlaufsdatei gestöbert und Folgendes gefunden:
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
)\"";}
Eigentlich ist es ziemlich gut. Ja, es verwendet eval
, übergibt ihm aber nie etwas anderes als einen numerischen Verweis auf seine Argumente. Es erstellt beliebige sed
Skripte für die Verarbeitung einer letzten Übereinstimmung. Ich zeige es Ihnen:
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...'
Das gibt das Folgende auf stderr aus. Dies ist eine Kopie der lmatch
Eingabe von :
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'
Die ed-Subshell der Funktion eval
durchläuft alle ihre Argumente einmal. Während sie diese durchläuft, iteriert sie je nach Kontext für jeden Schalter einen geeigneten Zähler und überspringt so viele Argumente für die nächste Iteration. Von da an führt sie pro Argument eine von mehreren Aktionen aus:
- Für jede Option fügt der Optionenparser .
$a
hinzu , basierend auf dem Wert von , der für jedes verarbeitete Argument um den Argumentzähler erhöht wird. wird einer der beiden folgenden Werte zugewiesen:$o
$a
$i
$a
a=$((i+=1))
- dies wird zugewiesen, wenn entweder einer Kurzoption das zugehörige Argument nicht angehängt ist oder wenn es sich bei der Option um eine Langoption handelte.a=$i#-?
- dieser Wert wird vergeben, wenn es sich um eine Short-Option handelt undtutsein Argument daran angehängt haben.a=\${$a}${1:+$d\${$(($1))\}}
- Unabhängig von der ursprünglichen Zuweisung$a
wird der Wert immer in Klammern eingeschlossen und ggf.-s
noch$i
einmal erhöht und ein zusätzlich abgegrenztes Feld angehängt.
Das Ergebnis ist, dass eval
niemals eine Zeichenfolge mit Unbekannten übergeben wird. Auf jedes der Befehlszeilenargumente wird durch seine numerische Argumentnummer verwiesen - sogar auf das Trennzeichen, das aus dem ersten Zeichen des ersten Arguments extrahiert wird und das einzige Mal ist, dass Sie ein Zeichen verwenden sollten, das nicht maskiert ist. Im Grunde ist die Funktion ein Makrogenerator - sie interpretiert die Werte der Argumente niemals auf besondere Weise, sed
da(und wird es natürlich auch)kann das problemlos handhaben, wenn es das Skript analysiert. Stattdessen ordnet es seine Argumente einfach sinnvoll in einem funktionsfähigen Skript an.
Hier ist eine Debug-Ausgabe der Funktion bei der Arbeit:
... 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
'
Damit lmatch
können Sie ganz einfach Regexes auf Daten anwenden, die der letzten Übereinstimmung in einer Datei folgen. Das Ergebnis des Befehls, den ich oben ausgeführt habe, ist:
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'
... das unter Berücksichtigung der Teilmenge der Dateieingabe, die auf die letzte /^.0/
Übereinstimmung folgt, die folgenden Ersetzungen anwendet:
sdd&&&&d
- wird$match
4-mal durch sich selbst ersetzt.sd'dsqd4
- das vierte einfache Anführungszeichen nach dem Zeilenanfang seit der letzten Übereinstimmung.sd"d\dqd2
– das Gleiche gilt, aber für Anführungszeichen und global.
lmatch
Um zu demonstrieren, wie man das letzte Komma in einer Datei entfernen kann, geht es wie folgt :
printf "%d, %d %d, %d\n" $(seq 5 5 100) |
lmatch '/\(.*\),' -r\\1
AUSGABE:
5, 10 15, 20
25, 30 35, 40
45, 50 55, 60
65, 70 75, 80
85, 90 95 100
Antwort4
sehenhttps://stackoverflow.com/questions/12390134/Komma aus der letzten Zeile entfernen
Das hat bei mir funktioniert:
$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"}
Am besten entferne ich die letzte Zeile und füge nach dem Entfernen des Kommas das Zeichen ] erneut hinzu.