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 ellert
tecken, och att ’ är vad GNUsed
gör det också närPOSIXLY_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 änread
. - 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 avread
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 kommandottr
, 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. ”