Hur trimmar jag ledande och efterföljande mellanslag från varje rad med en del utdata?

Jag vill ta bort alla ledande och efterföljande utrymmen och flikar från varje rad i en utdata.

Finns det ett enkelt verktyg som trim Jag kunde leda min utdata till?

Exempel på fil:

test space at back test space at front TAB at end TAB at front sequence of some space in the middle some empty lines with differing TABS and spaces: test space at both ends 

Kommentarer

  • Till alla som letar här efter en lösning för att ta bort nya rader, det är ett annat problem. Per definition skapar en ny rad en ny textrad. Därför kan en textrad inte innehålla en ny rad. Frågan du vill ställa är hur man tar bort en ny rad från början eller slutet av en sträng: stackoverflow.com/questions/369758 , eller hur man tar bort tomt rader eller rader som bara är blanksteg: serverfault.com/questions/252921

Svar

awk "{$1=$1;print}" 

eller kortare:

awk "{$1=$1};1" 

Skulle trimma ledning och efterföljande eller tabbtecken 1 och även kläm sekvenser av flikar och mellanslag i ett enda mellanslag.

Det fungerar för att när du tilldelar något till ett av fälten , bygger awk hela posten (som tryckt av print) genom att sammanfoga alla fält ($1, …, $NF) med OFS (mellanslag som standard).

1 (och eventuellt annat tomt tecken s beroende på plats och awk implementering)

Kommentarer

  • Semikolon på andra exemplet är överflödigt. Kan använda: awk '{$1=$1}1'
  • @Brian, nej, ; krävs i standard awk-syntax
  • Intressant … Ingen semikolon stöds av gawk, mawk och OS X ’ s awk. (Åtminstone för mina versioner (1.2, 4.1.1 respektive 20070501)
  • Det enda jag inte ’ tycker om det här tillvägagångssättet är att du tappa upprepade mellanslag inom raden. Till exempel echo -e 'foo \t bar' | awk '{$1=$1};1'
  • echo ' hello ' | xargs

Svar

Kommandot kan kondenseras som om du använder GNU sed:

$ sed "s/^[ \t]*//;s/[ \t]*$//" < file 

Exempel

Här är kommandot ovan i aktion.

$ echo -e " \t blahblah \t " | sed "s/^[ \t]*//;s/[ \t]*$//" blahblah 

Du kan använda hexdump för att bekräfta att sed kommandot tar bort de önskade tecknen korrekt.

$ echo -e " \t blahblah \t " | sed "s/^[ \t]*//;s/[ \t]*$//" | hexdump -C 00000000 62 6c 61 68 62 6c 61 68 0a |blahblah.| 00000009 

Teckenklasser

Du kan också använda teckenklassnamn istället för att bokstavligen lista uppsättningarna så här, [ \t]:

$ sed "s/^[[:blank:]]*//;s/[[:blank:]]*$//" < file 

Exempel

$ echo -e " \t blahblah \t " | sed "s/^[[:blank:]]*//;s/[[:blank:]]*$//" 

De flesta GNU-verktyg som använder regelbunden expre ssions (regex) stöder dessa klasser (här med deras motsvarighet i det typiska C-området för ett ASCII-baserat system (och endast där)).

 [[:alnum:]] - [A-Za-z0-9] Alphanumeric characters [[:alpha:]] - [A-Za-z] Alphabetic characters [[:blank:]] - [ \t] Space or tab characters only [[:cntrl:]] - [\x00-\x1F\x7F] Control characters [[:digit:]] - [0-9] Numeric characters [[:graph:]] - [!-~] Printable and visible characters [[:lower:]] - [a-z] Lower-case alphabetic characters [[:print:]] - [ -~] Printable (non-Control) characters [[:punct:]] - [!-/:-@[-`{-~] Punctuation characters [[:space:]] - [ \t\v\f\n\r] All whitespace chars [[:upper:]] - [A-Z] Upper-case alphabetic characters [[:xdigit:]] - [0-9a-fA-F] Hexadecimal digit characters 

Använda dessa i stället för bokstavliga uppsättningar verkar alltid som slöseri med utrymme, men om du är bekymrad över att din kod är bärbar eller har att göra med alternativa teckenuppsättningar (tänk internationellt), vill du troligen använda klassnamnen istället .

Referenser

Kommentarer

  • Observera att [[:space:]] inte motsvarar [ \t] allmänt fall (unicode, etc). [[:space:]] kommer förmodligen att vara mycket långsammare (eftersom det finns många fler typer av blanksteg i unicode än bara ' ' och '\t'). Samma sak för alla andra.
  • sed 's/^[ \t]*//' är inte bärbar. För närvarande kräver POSIX till och med att för att ta bort en sekvens av mellanslag, backslash eller t tecken, och att ’ är vad GNU sed gör det också när POSIXLY_CORRECT finns i miljön.
  • Vad händer om jag vill klippa nya rader? ’ \ n \ n text \ n \ n ’
  • Jag gillar sed-lösningen på grund av bristen på andra biverkningar som i awk-lösningen. Den första varianten fungerar inte när jag försökte den i bash på OSX jsut nu, men teckenklassversionen fungerar: sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//'
  • @EugeneBiryukov se min kommentar till det ursprungliga inlägget

Svar

xargs utan argument gör det.

Exempel:

trimmed_string=$(echo "no_trimmed_string" | xargs) 

Kommentarer

  • Detta kontraherar också flera mellanslag inom en rad som inte begärdes i frågan
  • @roaima – sant men det accepterade svaret pressar också mellanrum (vilket inte begärdes i frågan). Jag tror att det verkliga problemet här är att xargs misslyckas med att leverera om ingången innehåller snedstreck och enstaka citat.
  • @don_crissti som inte ’ t menar att det accepterade svaret svarar korrekt på frågan som ställd. Men i det här fallet flaggades det inte ’ som en varning medan det i det accepterade svaret var. Jag ’ har förhoppningsvis lyftt fram faktum om det ’ är av relevans för en framtida läsare.
  • Det har också bryter på enstaka citat, dubbla citat, bakåtvända tecken. Den kör också en eller flera echo anrop. Vissa ekimplementeringar kommer också att bearbeta alternativ och / eller snedstreck … Det fungerar också endast för enradig inmatning.

Svar

Som föreslagits av Stéphane Chazelas i det accepterade svaret kan du nu
skapa ett skript /usr/local/bin/trim:

#!/bin/bash awk "{$1=$1};1" 

och ge den filen körbara rättigheter:

chmod +x /usr/local/bin/trim 

Nu kan du skicka varje utdata till trim till exempel:

cat file | trim 

(för kommentarerna nedan: jag använde det här förut: while read i; do echo "$i"; done
vilket också fungerar bra, men är mindre performant)

Kommentarer

  • Lycka till om din fil är enorm och / eller innehåller snedstreck.
  • @don_crissti: kan du kommentera lite mer ?, vilken lösning skulle passar bättre för stora filer, och hur skulle jag kunna ändra min lösning om filen innehöll snedstreck?
  • Du ’ måste använda while read -r line för att bevara backslash och även då … . När det gäller enorma filer / hastighet valde du verkligen den värsta lösningen. Jag tror inte ’ där ’ är något värre där ute. Se svaren på Varför använder jag en shell-loop för att behandla text dålig praxis? inklusive min kommentar till det senaste svaret där jag lade till en länk till ett hastighetsindex. sed -svaren här är helt fina IMO och mycket bättre än read.
  • Du kan också lägga till ett alias i / etc / profil (eller din ~ / .bashrc eller ~ / .zshrc etc …) alias trim = ” awk ’ { \ $ 1 = \ $ 1}; 1 ’ ”
  • Inget behov av bash, du kan göra det #! /usr/bin/awk -f {$1=$1};1. (se upp för filnamn som innehåller = tecken men)

Svar

Om du lagrar rader som variabler kan du använda bash för att utföra jobbet:

ta bort ledande blanksteg från en sträng:

shopt -s extglob echo ${text##+([[:space:]])} 

ta bort efterföljande vitt utrymme från en sträng:

shopt -s extglob echo ${text%%+([[:space:]])} 

ta bort allt vitt utrymme från en sträng:

echo ${text//[[:space:]]} 

Kommentarer

  • Att ta bort alla blanksteg från en sträng är inte samma sak som att ta bort både ledande och efterföljande mellanslag (i fråga).
  • Den bästa lösningen – den kräver bara bash-inbyggda och inga externa processgafflar.
  • Trevligt. Skript kör mycket snabbare om de inte ’ inte behöver dra in externa program (som awk eller sed). Detta fungerar även med ” modern ” (93u +) versioner av ksh.

Svar

sed -e "s/^[[:space:]]*//" -e "s/[[:space:]]*$//" 

Om du läser en rad i en skalvariabel, read gör det redan om inte annat anges .

Kommentarer

  • +1 för read. Så om du rör till medan du läser fungerar det: cat file | while read i; do echo $i; done
  • @rubo förutom att i ditt exempel bearbetas även den icke citerade variabeln av skalet. Använd echo "$i" för att se den verkliga effekten av read

Svar

För att ta bort alla ledande och efterföljande utrymmen från en given rad tack vare ett ”piped” -verktyg kan jag identifiera 3 olika sätt som inte är helt ekvivalenta. Dessa skillnader gäller mellanrummen mellan inmatningsraden. Beroende på förväntat b ehaviour, du gör ditt val.

Exempel

För att förklara skillnaderna, låt oss överväga den här dummyinmatningsraden:

" \t A \tB\tC \t " 

tr

$ echo -e " \t A \tB\tC \t " | tr -d "[:blank:]" ABC 

tr är verkligen ett enkelt kommando. I det här fallet raderas alla mellanslag eller tabuleringstecken.

awk

$ echo -e " \t A \tB\tC \t " | awk "{$1=$1};1" A B C 

awk tar bort ledande och avgränsande mellanslag och klämmer till ett enda mellanslag varje mellanslag mellan ord.

sed

$ echo -e " \t A \tB\tC \t " | sed "s/^[ \t]*//;s/[ \t]*$//" A B C 

I det här fallet sed tar bort ledande och avgränsande mellanslag utan att röra några mellanslag mellan ord.

Anmärkning:

När det gäller ett ord per rad gör tr jobbet.

Kommentarer

  • Inget av detta trimmar efterföljande / ledande nya rader dock
  • +1 för en lista med lösningar med deras (ibland oväntade) utdata.
  • @ user61382 detta är ganska sent, men se min kommentar till det ursprungliga inlägget.
  • @highmaintenance: använd [:space:], istället för [: blank:], för kommandot tr, som: ... | tr -d [:space:], för att ta bort nya rader också. (se: man tr)

Svar

sed är en bra verktyg för det:

 # substitute ("s/") sed "s/^[[:blank:]]*//; # parts of lines that start ("^") with a space/tab s/[[:blank:]]*$//" # or end ("$") with a space/tab # with nothing (/) 

Du kan använda det för ditt fall vara antingen piping i texten, t.ex.

<file sed -e "s/^[[... 

eller genom att agera på det ”inline” om din sed är GNU: en:

sed -i "s/..." file 

men att ändra källan på det här sättet är ”farligt” eftersom det kan vara omöjligt att återställa när det inte fungerar rätt (eller till och med när det gör det!), så säkerhetskopiera först (eller använd -i.bak som också har fördelen att vara bärbar till vissa BSD sed s)!

Svar

Ett svar som du snabbt kan förstå:

#!/usr/bin/env python3 import sys for line in sys.stdin: print(line.strip()) 

Bonus: ersätt str.strip([chars]) med godtyckliga tecken för att trimma eller använda .lstrip() eller .rstrip() efter behov.

Gilla rubo77 ”sa nswer , spara som skript /usr/local/bin/trim och ge behörigheter med chmod +x.

Svar

Om strängen man försöker trimma är kort och kontinuerlig / sammanhängande kan man helt enkelt skicka den som en parameter till vilken bash-funktion som helst:

 trim(){ echo $@ } a=" some random string " echo ">>`trim $a`<<" Output >>some random string<< 

Svar

Jag skrev den här skalfunktionen med awk

awkcliptor(){ awk -e "BEGIN{ RS="^$" } {gsub(/^[\n\t ]*|[\n\t ]*$/,"");print ;exit}" "$1" ; } 

BEGIN{ RS="^$" }:
i början innan du börjar analysera set-post och separator till ingen dvs behandla hela inmatningen som en enda post

gsub(this,that):
ersätt denna regexp med den strängen

/^[\n\t ]*|[\n\t ]*$/:
för den strängen fångar upp ett föregående radlinjeutrymme och flikklass
eller postar nyradutrymme och flikklass och ersätter dem med en tom sträng

print;exit: skriv sedan ut och avsluta

"$1":
och skicka funktionens första argument till bli en process av awk

hur man använder:
kopiera ovan kod, klistra in i skalet och ange sedan för att definiera funktionen.
då kan du använda awkcliptor som ett kommando med första argumentet som inmatningsfil

exempelanvändning:

echo " ggggg " > a_file awkcliptor a_file 

utgång:

ggggg 

eller

echo -e "\n ggggg \n\n "|awkcliptor 

utgång:

ggggg 

Kommentarer

  • Kan du snälla förklara skillnaden till awk '{$1=$1};1'?

Svar

För de av oss utan tillräckligt med utrymme i hjärnan för att komma ihåg obskyra sed syntax, bara vänd strängen , skär det första fältet med en avgränsare för mellanslag och vänd tillbaka det igen.

cat file | rev | cut -d" " -f1 | rev 

Kommentarer

  • Detta fungerar bara om det inte finns mer än ett mellanslag som leder varje rad och inte mer än ett ord i någon rad.

Svar

trimpy () { python3 -c "import sys for line in sys.stdin: print(line.strip())" } trimsed () { gsed -e "s/^[[:space:]]*//" -e "s/[[:space:]]*$//" } trimzsh () { local out="$(</dev/stdin)" [[ "$out" =~ "^\s*(.*\S)\s*$" ]] && out="$match[1]" || out="" print -nr -- "$out" } # example usage echo " hi " | trimpy 

Bonus: ersätt str.strip([chars]) med godtyckliga tecken för att trimma eller använd eller .rstrip() efter behov.

Svar

översätt kommando skulle fungera

cat file | tr -d [:blank:] 

Kommentarer

  • Detta kommando är inte korrekt eftersom det tar bort alla mellanslag från filen, inte bara ledande / efterföljande mellanslag.
  • @BrianRedbeard Du har rätt. Detta är fortfarande ett användbart svar för en monolitisk sträng utan mellanslag.

Svar

för bash-exempel:

alias trim="awk "{\$1=\$1};1"" 

användning:

echo -e " hello\t\tkitty " | trim | hexdump -C 

resultat:

00000000 68 65 6c 6c 6f 20 6b 69 74 74 79 0a |hello kitty.| 0000000c 

Kommentarer

  • awk '{$1=$1};1' svaret gavs för länge sedan. Idén att skapa ett alias av det föreslogs i en kommentar nästan lika länge sedan. Ja, du får ta någon annans kommentar och göra den till ett svar. Men om du gör det, bör du ge kredit till de personer som publicerade idén före dig. Och det här är en så trivial förlängning av det accepterade svaret att det inte är värt besväret.
  • Idén var att skapa alias. Jag har inte ’ inte sett det svaret tidigare.
  • och andra sak från stacken: ” Tack för återkopplingen! Röster som avgivits av personer med mindre än 15 anseende registreras, men ändrar inte den offentligt visade poängen. ”

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *