Jag vill ta bort den sista kolumnen i en txt-fil, medan jag inte vet vad kolumnnumret är. Hur kunde jag göra det?
Exempel:
Ingång:
1223 1234 1323 ... 2222 123 1233 1234 1233 ... 3444 125 0000 5553 3455 ... 2334 222
Och jag vill att min produktion ska vara :
1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
Kommentarer
Svar
Med awk
:
awk "NF{NF-=1};1" <in >out
eller:
awk "NF{NF--};1" <in >out
eller:
awk "NF{--NF};1" <in >out
Även om det här ser ut som voodoo fungerar det. Det finns tre delar till var och en av dessa awk-kommandon.
Den första är NF
, vilket är en förutsättning för den andra delen. NF
är en variabel som innehåller antalet fält i en rad. I AWK är saker sanna om de ”inte är 0 eller tomma strängar ""
. Därför minskas den andra delen (där NF
minskas) händer bara om NF
inte är 0.
Den andra delen (antingen NF-=1
NF--
eller --NF
) subtraherar bara ett från variabeln NF
. Detta förhindrar att det sista fältet skrivs ut, för när du ändrar ett fält (tar bort det sista fältet i det här fallet), awk
omkonstruerar $0
, sammanfogar alla fält åtskilda av mellanslag som standard . $0
innehöll inte det sista fältet längre.
Den sista delen är 1
. Det är inte magiskt, det används bara som ett uttryck som betyder true
. Om ett awk
-uttryck utvärderas till sant utan någon associerad åtgärd är awk
standardåtgärd print $0
.
Kommentarer
- @JJoao: Ah, tack, glömde bort
--
. En anteckning, för närvarande, du behöver;1
för POSIX-kompatibel. - Min första instinkt skulle vara att använda en for-loop, men det här är mycket mer kortfattat och smart.
- Det är ' värt att notera att om du ' använder en icke-standardavgränsare, så ' Jag måste göra några ändringar. Förutsatt att
,
är din avgränsare:awk -F',' 'BEGIN { OFS = FS }; NF { NF -= 1 }; 1' < in > out
- Effekten av att minska NF är odefinierat beteende av POSIX – du får olika utdata beroende på vilket awk du ' körs. Vissa awks tar bort det sista fältet som du vill, andra gör ingenting alls, och andra kan rapportera ett syntaxfel eller så annat.
Svar
Använd grep
med PCRE:
$ grep -Po ".*(?=\s+[^\s]+$)" file.txt 1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
Använda GNU sed
:
$ sed -r "s/(.*)\s+[^\s]+$/\1/" file.txt 1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
Kommentarer
- @ramin Visst. .Kan du ställa den som en ny fråga (så fungerar den här webbplatsen) 🙂
- @ramin Ger det dig något tidsbegränsning eller någon varning?
- det står att detta är uteslutet ifrågasatt!
- @ramin Ok..låt mig kontakta en administratör, kanske de kan hjälpa dig med det .. btw kollade du någon gammal QA angående din fråga? det är en möjlighet att frågan redan är ställd och besvarad ..
- Don ' t ställa super grundläggande frågor som " hur kan jag byta namn på ett filnamn i Linux ". Använd Google.
Svar
Med Perl:
perl -lane "$,=" ";pop(@F);print(@F)" in
Använda rev
+ cut
:
rev in | cut -d " " -f 2- | rev
Svar
Med GNU sed:
sed -r "s/\s+\S+$//" input.txt
Mer allmänt, den här fungerar med BSD sed i OSX, liksom GNU sed:
sed "s/[[:space:]]\{1,\}[^[:space:]]\{1,\}$//" input.txt
Svar
Om avgränsaren alltid är en enda karaktär (så två eller flera på varandra följande avgränsare anger tomma fält) kan du head
bara den första raden från din inmatningsfil, räkna avgränsarna ( n
avgränsare betyder att antalet fält är n+1
) använd sedan cut
för att skriva ut från 1
st fält upp till n
fältet (näst sista), t.ex. med tabbavgränsad ingång:
n=$(head -n 1 infile | tr -dc \\t | tr \\t \\n | wc -l) cut -f1-$n infile > outfile
eller t.ex.med en csv fil:
n=$(head -n 1 infile | tr -dc , | tr , \\n | wc -l) cut -d, -f1-$n infile > outfile
Jag kommer att köra några riktmärken senare om jag har tid men med enorma insatser tror jag det här lösningen ska vara snabbare än andra lösningar som använder regex eftersom den här gör minimal bearbetning på första raden för att få antalet fält och använder sedan cut
som är optimerad för det här jobbet.
Svar
Du kan använda något av dessa:
sed "s/[[:space:]]*[^[:space:]]*$//" file awk "{sub(/[[:space:]]*[^[:space:]]*$/,"")}1" file
Svar
Använda vim:
Öppna fil i vim
vim <filename>
Gå till första raden, bara om markören placeras någon annanstans.
gg
Skapa ett makro med namnet ”q” qq
, som går till baksidan av den aktuella raden $
och går sedan tillbaka till det sista mellanslaget F
(huvudstad F, följt av bokstavligt mellanslag) ta sedan bort från aktuell position till slutet av raden D
gå ner till nästa rad j
och stoppa makroinspelning med q
.
qq$F Djq
Nu kan vi upprepa vårt makro med @q
för varje rad.
Vi kan också trycka på @@
för att upprepa sista makrot eller ännu enklare:
99@q
för att upprepa makrot 99 gånger.
Obs! Siffran får inte exakt matcha raderna.
Svar
För personer som har ett liknande problem men med olika fältseparatorer är detta awk
-metoden kommer att bevara fältseparatorn korrekt:
$ cat file foo.bar.baz baz.bar.foo $ awk -F"." "sub(FS $NF,x)" file foo.bar baz.bar
cut
låter som verktyget för jobbet.