Ik wil de laatste kolom van een txt-bestand verwijderen, terwijl ik niet weet wat het kolomnummer is is. Hoe kan ik dit doen?
Voorbeeld:
Invoer:
1223 1234 1323 ... 2222 123 1233 1234 1233 ... 3444 125 0000 5553 3455 ... 2334 222
En ik wil dat mijn uitvoer is :
1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
Reacties
Antwoord
Met awk
:
awk "NF{NF-=1};1" <in >out
of:
awk "NF{NF--};1" <in >out
of:
awk "NF{--NF};1" <in >out
Hoewel dit op voodoo lijkt, werkt het. Elk van deze awk-opdrachten bestaat uit drie delen.
Het eerste is NF
, wat een voorwaarde is voor het tweede deel. NF
is een variabele die het aantal velden op een regel bevat. In AWK zijn dingen waar als ze “re niet 0 of lege string ""
zijn. Vandaar dat het tweede deel (waarbij NF
wordt verlaagd) gebeurt alleen als NF
niet 0 is.
Het tweede deel (ofwel NF-=1
NF--
of --NF
) trekt er gewoon een af van de NF
variabele. Dit voorkomt dat het laatste veld wordt afgedrukt, want wanneer je wijzigt een veld (verwijdert in dit geval het laatste veld), awk
herbouw $0
, voeg standaard alle velden samen die door een spatie zijn gescheiden . $0
bevatte het laatste veld niet meer.
Het laatste deel is 1
. Het is niet magisch, het wordt alleen gebruikt als een uitdrukking die true
betekent. Als een awk
uitdrukking naar true evalueert zonder enige bijbehorende actie, is awk
standaardactie print $0
.
Reacties
- @JJoao: Ah, bedankt, ben
--
vergeten. Een opmerking, momenteel heb je;1
nodig voor POSIX-compliant. - Mijn aanvankelijke instinct zou zijn om een for-lus te gebruiken, maar dit is veel beknopter en slimmer.
- Het ' is het vermelden waard dat als u ' een niet-standaard scheidingsteken gebruikt, u ' Ik zal enkele wijzigingen moeten aanbrengen. Ervan uitgaande dat
,
uw scheidingsteken is:awk -F',' 'BEGIN { OFS = FS }; NF { NF -= 1 }; 1' < in > out
- Het effect van het verlagen van NF is ongedefinieerd gedrag door POSIX – u krijgt verschillende uitvoer afhankelijk van welke awk u ' opnieuw draait. Sommige awks zullen het laatste veld verwijderen zoals je wilt, sommige doen helemaal niets, en anderen kunnen een syntaxisfout rapporteren of iets anders.
Antwoord
grep
gebruiken met PCRE:
$ grep -Po ".*(?=\s+[^\s]+$)" file.txt 1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
GNU gebruiken sed
:
$ sed -r "s/(.*)\s+[^\s]+$/\1/" file.txt 1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
Reacties
- @ramin Zeker. . Kunt u het alstublieft stellen als een nieuwe vraag (zo werkt deze site) 🙂
- @ramin Geeft het u enige tijdsbeperking of enige waarschuwing?
- er staat dat dit geen standaardvraag is!
- @ramin Ok..laat me contact opnemen met een beheerder, misschien kunnen zij je ermee helpen .. btw heb je een oude QA gecontroleerd met betrekking tot je vraag? het is een mogelijkheid dat de vraag al is gesteld en beantwoord.
- Don ' stel geen super eenvoudige vragen zoals " hoe kan ik een bestandsnaam hernoemen in Linux ". Gebruik Google.
Answer
Perl gebruiken:
perl -lane "$,=" ";pop(@F);print(@F)" in
Met rev
+ cut
:
rev in | cut -d " " -f 2- | rev
Answer
GNU gebruiken sed:
sed -r "s/\s+\S+$//" input.txt
Meer in het algemeen, deze werkt met de BSD sed in OSX, evenals GNU sed:
sed "s/[[:space:]]\{1,\}[^[:space:]]\{1,\}$//" input.txt
Answer
Als het scheidingsteken altijd een enkel teken is (dus twee of meer opeenvolgende scheidingstekens duiden lege velden aan), kunt u head
alleen de eerste regel uit uw invoerbestand tellen, de scheidingstekens tellen ( n
scheidingstekens betekent dat het aantal velden n+1
is) en gebruik vervolgens cut
om af te drukken vanaf de 1
st veld tot aan het n
veld (voorlaatste), bijv. met door tabs gescheiden invoer:
n=$(head -n 1 infile | tr -dc \\t | tr \\t \\n | wc -l) cut -f1-$n infile > outfile
of bijv.met een csv -bestand:
n=$(head -n 1 infile | tr -dc , | tr , \\n | wc -l) cut -d, -f1-$n infile > outfile
Ik zal later wat benchmarks uitvoeren als ik de tijd heb, maar met enorme input denk ik dit oplossing zou sneller moeten zijn dan andere oplossingen die regex gebruiken, aangezien deze minimale verwerking op de eerste regel uitvoert om het aantal velden te krijgen en vervolgens cut
gebruikt dat is geoptimaliseerd voor deze taak.
Antwoord
Draagbaar kunt u een van deze gebruiken:
sed "s/[[:space:]]*[^[:space:]]*$//" file awk "{sub(/[[:space:]]*[^[:space:]]*$/,"")}1" file
Antwoord
Met vim:
Open bestand in vim
vim <filename>
Ga naar de eerste rij, voor het geval de cursor ergens anders staat.
gg
Maak een macro met de naam “q” qq
, dat naar de achterkant van de huidige regel gaat $
, en dan teruggaat naar de laatste spatie F
(hoofdletter F, gevolgd door letterlijke SPATIE) en vervolgens verwijderen vanaf de huidige positie tot het einde van de regel D
ga naar de volgende regel j
en stop de macro-opname met q
.
qq$F Djq
Nu kunnen we onze macro herhalen met @q
voor elke regel.
We kunnen ook op @@
om de laatste macro te herhalen of nog gemakkelijker:
99@q
om de macro 99 keer te herhalen.
Opmerking: het nummer mag niet exact overeenkomen met de regels.
Antwoord
Voor mensen met een soortgelijk probleem maar met verschillende veldscheidingstekens is dit awk
methode zal het veldscheidingsteken correct bewaren:
$ cat file foo.bar.baz baz.bar.foo $ awk -F"." "sub(FS $NF,x)" file foo.bar baz.bar
cut
klinkt als de tool voor de klus.