Jeg vil slette den sidste kolonne i en txt-fil, mens jeg ikke ved, hvad kolonnenummeret er. Hvordan kunne jeg gøre dette?
Eksempel:
Input:
1223 1234 1323 ... 2222 123 1233 1234 1233 ... 3444 125 0000 5553 3455 ... 2334 222
Og jeg ønsker, at min output 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
Selvom dette ligner voodoo, fungerer det. Der er tre dele til hver af disse awk-kommandoer.
Den første er NF
, hvilket er en forudsætning for den anden del. NF
er en variabel, der indeholder antallet af felter i en linje. I AWK er tingene sandt, hvis de “ikke er 0 eller tom streng ""
. Derfor er den anden del (hvor NF
reduceres) sker kun, hvis NF
ikke er 0.
Den anden del (enten NF-=1
NF--
eller --NF
) trækker bare en fra variablen NF
. Dette forhindrer det sidste felt i at blive udskrevet, for når du ændrer et felt (fjerner det sidste felt i dette tilfælde), awk
re-konstruerer $0
, sammenkæder alle felter adskilt af mellemrum som standard . $0
indeholdt ikke det sidste felt længere.
Den sidste del er 1
. Det er ikke magisk, det bruges bare som et udtryk, der betyder true
. Hvis et awk
-udtryk evalueres til sandt uden nogen tilknyttet handling, er awk
standardhandling print $0
.
Kommentarer
- @JJoao: Ah, tak, glemte
--
. En note, i øjeblikket skal du have;1
til POSIX-kompatibel. - Mit første instinkt ville være at bruge en for-loop, men dette er meget mere kortfattet og klogt.
- Det er ' værd at bemærke, at hvis du ' bruger en ikke-standardafgrænser, skal du ' Jeg skal foretage nogle ændringer. Under forudsætning af at
,
er din afgrænser:awk -F',' 'BEGIN { OFS = FS }; NF { NF -= 1 }; 1' < in > out
- Effekten af dekrementering af NF er udefineret adfærd fra POSIX – du får forskellig output afhængigt af, hvilket awk du ' kører igen. Nogle awks fjerner det sidste felt, som du vil, andre vil slet ikke gøre noget, og andre kan rapportere en syntaksfejl eller noget andet.
Svar
Brug af grep
med PCRE:
$ grep -Po ".*(?=\s+[^\s]+$)" file.txt 1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
Brug af GNU sed
:
$ sed -r "s/(.*)\s+[^\s]+$/\1/" file.txt 1223 1234 1323 ... 2222 1233 1234 1233 ... 3444 0000 5553 3455 ... 2334
Kommentarer
- @ramin Sikker. .Kan du spørge det som et nyt spørgsmål (sådan fungerer dette site) 🙂
- @ramin Giver det dig noget tidsbegrænsning eller nogen advarsel?
- der står, at dette ikke er standardspørgsmål!
- @ramin Ok.. Lad mig kontakte en administrator, måske kan de hjælpe dig med det .. btw har du tjekket en gammel QA vedrørende dit spørgsmål? det er en mulighed for, at spørgsmålet allerede er stillet og besvaret ..
- Don ' t stille super grundlæggende spørgsmål som " hvordan kan jeg omdøbe et filnavn i Linux ". Brug Google.
Svar
Brug af Perl:
perl -lane "$,=" ";pop(@F);print(@F)" in
Brug af rev
+ cut
:
rev in | cut -d " " -f 2- | rev
Svar
Brug af GNU sed:
sed -r "s/\s+\S+$//" input.txt
Mere generelt er denne fungerer med BSD sed i OSX samt GNU sed:
sed "s/[[:space:]]\{1,\}[^[:space:]]\{1,\}$//" input.txt
Svar
Hvis afgrænseren altid er en enkelt tegn (så to eller flere på hinanden følgende afgrænsere angiver tomme felter), kan du head
bare den første linje fra din inputfil, tæl afgrænserne ( n
afgrænsere betyder, at antallet af felter er n+1
) brug derefter cut
til at udskrive fra 1
st felt op til n
th felt (næstsidste), f.eks. med tabulatorafgrænset input:
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 kører nogle benchmarks senere, hvis jeg har tid, men med enorme input tror jeg dette løsningen skal være hurtigere end andre løsninger, der bruger regex, da denne udfører minimal behandling på første linje for at få antallet af felter og derefter bruger cut
, som er optimeret til dette job.
Svar
Bærbart kan du bruge en af disse:
sed "s/[[:space:]]*[^[:space:]]*$//" file awk "{sub(/[[:space:]]*[^[:space:]]*$/,"")}1" file
Svar
Brug af vim:
Åbn fil i vim
vim <filename>
Gå til første række, bare hvis markøren er placeret et andet sted.
gg
Opret en makro med navnet “q” qq
, der går bag på den aktuelle linje $
, og går derefter tilbage til det sidste mellemrum F
(kapital F, efterfulgt af bogstavelig MELLEMRUM) slet derefter fra nuværende position til slutningen af linjen D
gå ned til næste linje j
og stop makrooptagelse med q
.
qq$F Djq
Nu kan vi gentage vores makro med @q
for hver linje.
Vi kan også trykke på @@
for at gentage den sidste makro eller endnu lettere:
99@q
for at gentage makroen 99 gange.
Bemærk: Nummeret må ikke ligefrem matche linjerne.
Svar
For folk, der har et lignende problem, men med forskellige feltadskillere, er dette awk
metode bevarer feltudskilleren korrekt:
$ cat file foo.bar.baz baz.bar.foo $ awk -F"." "sub(FS $NF,x)" file foo.bar baz.bar
cut
lyder som værktøjet til jobbet.