Jeg vil slette den siste kolonnen i en txt-fil, mens jeg ikke vet hva kolonnenummeret er. Hvordan kunne jeg gjøre dette?
Eksempel:
Inngang:
1223 1234 1323 ... 2222 123 1233 1234 1233 ... 3444 125 0000 5553 3455 ... 2334 222
Og jeg vil at utdataene mine skal være :
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
Selv om dette ser ut som voodoo, fungerer det. Det er tre deler til hver av disse awk-kommandoene.
Den første er NF
, som er en forutsetning for den andre delen. NF
er en variabel som inneholder antall felt i en linje. I AWK er ting sant hvis de «ikke er 0 eller tom streng ""
. Derfor blir den andre delen (der NF
blir redusert) skjer bare hvis NF
ikke er 0.
Den andre delen (enten NF-=1
NF--
eller --NF
) trekker bare en fra NF
-variabelen. Dette forhindrer at det siste feltet blir skrevet ut, for når du endrer et felt (fjerner det siste feltet i dette tilfellet), awk
re-konstruerer $0
, sammenføyer alle feltene atskilt med mellomrom som standard . $0
inneholdt ikke det siste feltet lenger.
Den siste delen er 1
. Det er ikke magisk, det blir bare brukt som et uttrykk som betyr true
. Hvis et awk
-uttrykk evalueres til sant uten noen tilknyttet handling, er awk
standardhandling print $0
.
Kommentarer
- @JJoao: Ah, takk, glemte
--
. Et notat, for øyeblikket, trenger du;1
for POSIX-kompatibel. - Mitt første instinkt ville være å bruke en for loop, men dette er mye mer konsist og smart.
- Det er ' det er verdt å merke seg at hvis du ' bruker en ikke-standard skilletegn, vil du ' Jeg må gjøre noen endringer. Forutsatt at
,
er skillelinjen din:awk -F',' 'BEGIN { OFS = FS }; NF { NF -= 1 }; 1' < in > out
- Effekten av å redusere NF er udefinert oppførsel av POSIX – du får forskjellig utgang, avhengig av hvilken uke du ' kjører. Noen awks vil fjerne det siste feltet som du vil, noen vil ikke gjøre noe i det hele tatt, og andre kan rapportere en syntaksfeil eller noe annet.
Svar
Bruk av grep
med PCRE:
$ grep -Po ".*(?=\s+[^\s]+$)" file.txt 1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
Bruk av GNU sed
:
$ sed -r "s/(.*)\s+[^\s]+$/\1/" file.txt 1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
Kommentarer
- @ramin Klart. .Kan du spørre det som et nytt spørsmål (slik fungerer dette nettstedet) 🙂
- @ramin Gir det deg noe tidsbegrensning eller noen advarsler?
- det står at dette er utenfor standard spørsmål!
- @ramin Ok..La meg kontakte en administrator, kan det være de kan hjelpe deg med det .. btw sjekket du noen gamle QA angående spørsmålet ditt? det er en mulighet for at spørsmålet allerede er stilt og besvart ..
- Don ' t stille super grunnleggende spørsmål som " hvordan kan jeg gi nytt navn til et filnavn i Linux ". Bruk Google.
Svar
Bruker Perl:
perl -lane "$,=" ";pop(@F);print(@F)" in
Bruke rev
+ cut
:
rev in | cut -d " " -f 2- | rev
Svar
Bruk av GNU sed:
sed -r "s/\s+\S+$//" input.txt
Mer generelt, denne fungerer med BSD sed i OSX, samt GNU sed:
sed "s/[[:space:]]\{1,\}[^[:space:]]\{1,\}$//" input.txt
Svar
Hvis avgrenseren alltid er en enkelt tegn (så to eller flere påfølgende avgrensere angir tomme felt), kan du head
bare den første linjen fra inndatafilen din, telle skillelinjene ( n
skilletegn betyr at antall felt er n+1
) bruk deretter cut
for å skrive ut fra 1
st felt opp til n
th feltet (nest siste), f.eks med tabulatoravgrenset inngang:
n=$(head -n 1 infile | tr -dc \\t | tr \\t \\n | wc -l) cut -f1-$n infile > outfile
eller f.eks.med en csv fil:
n=$(head -n 1 infile | tr -dc , | tr , \\n | wc -l) cut -d, -f1-$n infile > outfile
Jeg kjører noen referanser senere hvis jeg har tid, men med store innspill tror jeg dette løsningen skal være raskere enn andre løsninger som bruker regex ettersom denne utfører minimal behandling på første linje for å få antall felt og bruker deretter cut
som er optimalisert for denne jobben.
Svar
Portabelt kan du bruke en av disse:
sed "s/[[:space:]]*[^[:space:]]*$//" file awk "{sub(/[[:space:]]*[^[:space:]]*$/,"")}1" file
Svar
Bruk av vim:
Åpne fil i vim
vim <filename>
Gå til første rad, bare i tilfelle markøren plasseres noe annet sted.
gg
Opprett en makro med navnet «q» qq
, som går til baksiden av den nåværende linjen $
, og går deretter tilbake til siste mellomrom F
(hovedstad F, etterfulgt av bokstavelig SPACE), slett deretter fra nåværende posisjon til slutten av linjen D
gå ned til neste linje j
og stopp makroopptak med q
.
qq$F Djq
Nå kan vi gjenta makroen vår med @q
for hver linje.
Vi kan også trykke @@
for å gjenta siste makro eller enda enklere:
99@q
for å gjenta makroen 99 ganger.
Merk: Antallet må ikke stemme overens med linjene.
Svar
For folk som har et lignende problem, men med forskjellige feltskillere, er dette awk
-metoden vil bevare feltutskilleren riktig:
$ cat file foo.bar.baz baz.bar.foo $ awk -F"." "sub(FS $NF,x)" file foo.bar baz.bar
cut
høres ut som verktøyet for jobben.