Ich habe eine Datei mit vielen Zeilen in jeder Zeile. Es gibt viele Spalten (Felder), die durch Leerzeichen getrennt sind. Die Anzahl der Spalten in jeder Zeile ist unterschiedlich. Ich möchte die ersten beiden Spalten entfernen. Wie geht das?
73
Antworten:
Sie können es tun mit
cut
:cut -d " " -f 3- input_filename > output_filename
Erläuterung:
cut
: Rufen Sie den Befehl cut auf-d " "
: Verwenden Sie ein einzelnes Leerzeichen als Trennzeichen (cut
verwendet standardmäßig TAB)-f
: Felder angeben, die beibehalten werden sollen3-
: Alle Felder beginnend mit Feld 3input_filename
: Verwenden Sie diese Datei als Eingabe> output_filename
: Schreibe die Ausgabe in diese Datei.Alternativ können Sie dies tun mit
awk
:awk '{$1=""; $2=""; sub(" ", " "); print}' input_filename > output_filename
Erläuterung:
awk
: Rufen Sie den Befehl awk auf$1=""; $2="";
: Setzen Sie Feld 1 und 2 auf die leere Zeichenfolgesub(...);
: Bereinigen Sie die Ausgabefelder, da die Felder 1 und 2 weiterhin durch "" begrenzt werden.print
: Drucke die geänderte Zeileinput_filename > output_filename
: das gleiche wie oben.quelle
cut
standardmäßig tab als Trennzeichen verwendet. Siehe aktualisierte Antwort - gerade getestet und es funktioniert. Wenn alles andere gleich ist, würde ich empfehlen,cut
over zu verwendenawk
.awk '{sub(/([^ ]+ ){2}/, "")}1'
. Ich bin damit einverstanden, dass Schnitt sowieso die bessere Wahl ist, wenn Sie ein Einzelzeichen-Feldtrennzeichen haben.awk '{$1=""; $2=""; sub(/^ +/, ""); print}'
stattdessen oder kürzerawk '{$1=$2=""; sub(/^ +/, "")}1'
Hier ist eine Möglichkeit, dies mit Awk zu tun, die relativ einfach zu verstehen ist:
awk '{print substr($0, index($0, $3))}'
Dies ist ein einfacher awk-Befehl ohne Muster, sodass
{}
für jede Eingabezeile eine Aktion ausgeführt wird.Die Aktion besteht darin, den Teilstring einfach beginnend mit der Position des 3. Felds zu drucken.
$0
: die gesamte Eingabezeile$3
: 3. Feldindex(in, find)
: Gibt die Position vonfind
in string zurückin
substr(string, start)
: Rückgabe eines Teilstrings ab Indexstart
Wenn Sie ein anderes Trennzeichen wie Komma verwenden möchten, können Sie es mit der Option -F angeben:
awk -F"," '{print substr($0, index($0, $3))}'
Sie können dies auch für eine Teilmenge der Eingabezeilen ausführen, indem Sie vor der Aktion in ein Muster angeben
{}
. Nur Linien, die dem Muster entsprechen, werden ausgeführt.awk 'pattern{print substr($0, index($0, $3))}'
Wo Muster etwas sein kann wie:
/abcdef/
: Verwenden Sie einen regulären Ausdruck, arbeitet standardmäßig mit $ 0.$1 ~ /abcdef/
: auf einem bestimmten Feld arbeiten.$1 == blabla
: String-Vergleich verwendenNR > 1
: Datensatz- / Zeilennummer verwendenNF > 0
: Feld- / Spaltennummer verwendenquelle
Vielen Dank für die Veröffentlichung der Frage. Ich möchte auch das Skript hinzufügen, das mir geholfen hat.
awk '{ $1=""; print $0 }' file
quelle
OFS=FS
, um die Trennzeichen beizubehaltenawk '{$1=$2="";$0=$0;$1=$1}1'
Eingang
a b c d
Ausgabe
c d
quelle
$0=$0;$1=$1
dass die Leerzeichen verschwindenSie können verwenden
sed
:sed 's/^[^ ][^ ]* [^ ][^ ]* //'
Dies sucht nach Zeilen, die mit einem oder mehreren Nicht-Leerzeichen, einem Leerzeichen, einem weiteren Satz von einem oder mehreren Nicht-Leerzeichen und einem weiteren Leerzeichen beginnen, und löscht das übereinstimmende Material, auch bekannt als die ersten beiden Felder. Das
[^ ][^ ]*
ist geringfügig kürzer als die entsprechende, aber explizitere[^ ]\{1,\}
Notation, und das zweite kann auf Probleme mit GNU stoßensed
(wenn Sie es--posix
als Option verwenden,sed
kann es sogar GNU nicht vermasseln). OTOH, wenn die zu wiederholende Zeichenklasse komplexer war, gewinnt die nummerierte Notation der Kürze halber. Es ist einfach, dies zu erweitern, um "Leerzeichen oder Tabulatoren" als Trennzeichen oder "mehrere Leerzeichen" oder "mehrere Leerzeichen oder Registerkarten" zu behandeln. Es kann auch geändert werden, um optionale führende Leerzeichen (oder Tabulatoren) vor dem ersten Feld usw. zu behandeln.Für
awk
undcut
finden Sampson-Chen ‚s Antwort . Es gibt andere Möglichkeiten, dasawk
Skript zu schreiben , aber sie sind nicht wesentlich besser als die gegebene Antwort. Beachten Sie, dass Sie das Feldtrennzeichen möglicherweise explizit (-F" "
) festlegen müssen,awk
wenn Sie nicht möchten, dass Registerkarten als Trennzeichen behandelt werden, oder wenn zwischen den Feldern mehrere Leerzeichen stehen. Der POSIX-Standardcut
unterstützt nicht mehrere Trennzeichen zwischen Feldern. GNUcut
verfügt über die nützliche, aber nicht standardmäßige-i
Option, um mehrere Trennzeichen zwischen Feldern zuzulassen.Sie können es auch in reiner Schale tun:
while read junk1 junk2 residue do echo "$residue" done < in-file > out-file
quelle
residue
ein Backslash enthalten kann, wird dieser durch den obigen Lesevorgang interpretiert und nicht in der Ausgabe reproduziert. Immer benutzenwhile IFS= read -r ...
.bash
der Inhalt mit einer Ebene interpretiert wirdread
,bash
ist er (erneut) fehlerhaft. Der Lesebefehl in Original-Shells hat keinen solchen Unsinn gemacht. Ich glaube nicht, dass es von der POSIX-Shell benötigt wird. Es würde mich irritieren, wenn ich finde, dassbash
es das tut, was Sie sagen - ich habe bereits eine Hassliebe zu dem Programm, da es viele Dinge gut macht, aber es gibt einige Dinge, die es schlecht macht und sich ändert Legacy-Verhalten ist eines der schlimmsten, und die Anforderung einer Option zur Aktivierung des alten Standardverhaltens ist ... sehr irritierend. Es scheint, du hast recht;bash
ist gegabelt!residue
bei Feld 4 (oder höher) anstelle von Feld 3 beginnen würde .bash
folgt aber POSIX 2008. Ich wollte diese Funktionalität in mehr als einem Vierteljahrhundert der Shell-Programmierung nie, aber ich denke, ich muss in einer Minderheit sein.Es ist ziemlich einfach, es nur mit Shell zu machen
while read A B C; do echo "$C" done < oldfile >newfile
quelle
read -r
anstelle von verwenden möchtenread
.read -r
bewahrt Backslashes.read
wird nicht. Zum Beispiel:echo "foo ba\r"
erzeugt eine Ausgabe vonfoo ba\r
. Allerdingsecho "foo ba\r" | (while read first_column second_column; do echo "$second_column"; done)
produziert nurbar
als der Ausgang (mit dem umgekehrten Schrägstrich entfernt , um das Hinzufügen von .-r
Flagge der korrekte Ausgabe von produziertba\r
Perl:
perl -lane 'print join(' ',@F[2..$#F])' File
awk:
awk '{$1=$2=""}1' File
quelle
Dies könnte für Sie funktionieren (GNU sed):
sed -r 's/^([^ ]+ ){2}//' file
oder für Spalten, die durch einen oder mehrere Leerzeichen getrennt sind:
sed -r 's/^(\S+\s+){2}//' file
quelle
Wenn Sie awk verwenden und auf einigen der folgenden Optionen basieren, wird die Verwendung einer for-Schleife etwas flexibler. Manchmal möchte ich vielleicht die ersten 9 Spalten löschen (wenn ich zum Beispiel ein "ls -lrt" mache), also ändere ich die 2 gegen eine 9 und das war's:
awk '{ for(i=0;i++<2;){$i=""}; print $0 }' your_file.txt
quelle
Verwenden Sie kscript
kscript 'lines.split().select(-1,-2).print()' file
quelle