Hoe kan ik witruimte van een Bash-variabele bijsnijden?

Ik heb een shellscript met deze code:

var=`hg st -R "$path"`
if [ -n "$var" ]; then
    echo $var
fi

Maar de voorwaardelijke code wordt altijd uitgevoerd, omdat hg staltijd ten minste één teken voor een nieuwe regel afdrukt.

  • Is er een eenvoudige manier om witruimte te verwijderen van $var(zoals trim()in PHP)?

of

  • Is er een standaardmanier om met dit probleem om te gaan?

Ik zou sedof AWK, maar ik zou graag denken dat er een elegantere oplossing voor dit probleem is.


Antwoord 1, autoriteit 100%

Een eenvoudig antwoord is:

echo "   lol  " | xargs

Xargszal het bijsnijden voor je doen. Het is één commando/programma, geen parameters, geeft de getrimde string terug, zo simpel is het!

Opmerking: hiermee worden niet alle interne spaties verwijderd, dus "foo bar"blijft hetzelfde; het wordt NIET "foobar". Meerdere spaties worden echter samengevoegd tot enkele spaties, dus "foo bar"wordt "foo bar". Bovendien worden tekens aan het einde van de regel niet verwijderd.


Antwoord 2, autoriteit 90%

Laten we een variabele definiëren die voorloop-, volg- en tussenliggende witruimte bevat:

FOO=' test test test '
echo -e "FOO='${FOO}'"
# > FOO=' test test test '
echo -e "length(FOO)==${#FOO}"
# > length(FOO)==16

Hoe alle WhiteSpace verwijderen (aangegeven met [:space:]in tr):

FOO=' test test test '
FOO_NO_WHITESPACE="$(echo -e "${FOO}" | tr -d '[:space:]')"
echo -e "FOO_NO_WHITESPACE='${FOO_NO_WHITESPACE}'"
# > FOO_NO_WHITESPACE='testtesttest'
echo -e "length(FOO_NO_WHITESPACE)==${#FOO_NO_WHITESPACE}"
# > length(FOO_NO_WHITESPACE)==12

Alleen voorloopwhites verwijderen:

FOO=' test test test '
FOO_NO_LEAD_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//')"
echo -e "FOO_NO_LEAD_SPACE='${FOO_NO_LEAD_SPACE}'"
# > FOO_NO_LEAD_SPACE='test test test '
echo -e "length(FOO_NO_LEAD_SPACE)==${#FOO_NO_LEAD_SPACE}"
# > length(FOO_NO_LEAD_SPACE)==15

Alleen Trailing WhiteSpace verwijderen:

FOO=' test test test '
FOO_NO_TRAIL_SPACE="$(echo -e "${FOO}" | sed -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_TRAIL_SPACE='${FOO_NO_TRAIL_SPACE}'"
# > FOO_NO_TRAIL_SPACE=' test test test'
echo -e "length(FOO_NO_TRAIL_SPACE)==${#FOO_NO_TRAIL_SPACE}"
# > length(FOO_NO_TRAIL_SPACE)==15

Hoe verwijdert u zowel toonaangevende als achterliggende spaties – ketting de seds:

FOO=' test test test '
FOO_NO_EXTERNAL_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_EXTERNAL_SPACE='${FOO_NO_EXTERNAL_SPACE}'"
# > FOO_NO_EXTERNAL_SPACE='test test test'
echo -e "length(FOO_NO_EXTERNAL_SPACE)==${#FOO_NO_EXTERNAL_SPACE}"
# > length(FOO_NO_EXTERNAL_SPACE)==14

Als je bash dit ondersteunt, kun je ook echo -e "${FOO}" | sed ...met sed ... <<<${FOO}, zoals zo (voor achterliggende witruimte):

FOO_NO_TRAIL_SPACE="$(sed -e 's/[[:space:]]*$//' <<<${FOO})"

Antwoord 3, autoriteit 35%

Er is een oplossing die alleen ingebouwde Bash gebruikt, genaamd jokertekens:

var="    abc    "
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"   
printf '%s' "===$var==="

Hier is hetzelfde verpakt in een functie:

trim() {
    local var="$*"
    # remove leading whitespace characters
    var="${var#"${var%%[![:space:]]*}"}"
    # remove trailing whitespace characters
    var="${var%"${var##*[![:space:]]}"}"   
    printf '%s' "$var"
}

Je geeft de tekenreeks die moet worden bijgesneden, tussen aanhalingstekens door. bijv.:

trim "   abc   "

Het leuke van deze oplossing is dat deze werkt met elke POSIX-compatibele shell.

Referentie


4, Autoriteit 6%

Om alle ruimtes vanaf het begin en het einde van een tekenreeks (inclusief einde van lijntekens) te verwijderen:

echo $variable | xargs echo -n

Hiermee wordt dubbele spaties ook weergegeven:

echo "  this string has a lot       of spaces " | xargs echo -n

produceert: ‘Deze string heeft veel spaties’


5, Autoriteit 5%

Strip één toonaangevende en één trailingruimte

trim()
{
    local trimmed="$1"
    # Strip leading space.
    trimmed="${trimmed## }"
    # Strip trailing space.
    trimmed="${trimmed%% }"
    echo "$trimmed"
}

Bijvoorbeeld:

test1="$(trim " one leading")"
test2="$(trim "one trailing ")"
test3="$(trim " one leading and one trailing ")"
echo "'$test1', '$test2', '$test3'"

Uitgang:

'one leading', 'one trailing', 'one leading and one trailing'

Strip allevoor- en achterspaties

trim()
{
    local trimmed="$1"
    # Strip leading spaces.
    while [[ $trimmed == ' '* ]]; do
       trimmed="${trimmed## }"
    done
    # Strip trailing spaces.
    while [[ $trimmed == *' ' ]]; do
        trimmed="${trimmed%% }"
    done
    echo "$trimmed"
}

Bijvoorbeeld:

test4="$(trim "  two leading")"
test5="$(trim "two trailing  ")"
test6="$(trim "  two leading and two trailing  ")"
echo "'$test4', '$test5', '$test6'"

Uitvoer:

'two leading', 'two trailing', 'two leading and two trailing'

Antwoord 6, autoriteit 4%

Uit de Bash Guide-sectie over globbing

Een extglob gebruiken in een parameteruitbreiding

#Turn on extended globbing  
shopt -s extglob  
 #Trim leading and trailing whitespace from a variable  
x=${x##+([[:space:]])}; x=${x%%+([[:space:]])}  
 #Turn off extended globbing  
shopt -u extglob  

Hier is dezelfde functionaliteit verpakt in een functie (OPMERKING: invoertekenreeks die aan functie is doorgegeven moet worden geciteerd):

trim() {
    # Determine if 'extglob' is currently on.
    local extglobWasOff=1
    shopt extglob >/dev/null && extglobWasOff=0 
    (( extglobWasOff )) && shopt -s extglob # Turn 'extglob' on, if currently turned off.
    # Trim leading and trailing whitespace
    local var=$1
    var=${var##+([[:space:]])}
    var=${var%%+([[:space:]])}
    (( extglobWasOff )) && shopt -u extglob # If 'extglob' was off before, turn it back off.
    echo -n "$var"  # Output trimmed string.
}

Gebruik:

string="   abc def ghi  ";
#need to quote input-string to preserve internal white-space if any
trimmed=$(trim "$string");  
echo "$trimmed";

Als we de functie wijzigen om in een subshell te worden uitgevoerd, hoeven we ons geen zorgen te maken over het onderzoeken van de huidige Shell-optie voor extgglob, kunnen we het gewoon instellen zonder de huidige schaal te beïnvloeden. Dit vereenvoudigt de functie enorm. Ik update ook de positionele parameters “op zijn plaats”, dus ik heb zelfs geen lokale variabele nodig

trim() {
    shopt -s extglob
    set -- "${1##+([[:space:]])}"
    printf "%s" "${1%%+([[:space:]])}" 
}

Dus:

$ s=$'\t\n \r\tfoo  '
$ shopt -u extglob
$ shopt extglob
extglob         off
$ printf ">%q<\n" "$s" "$(trim "$s")"
>$'\t\n \r\tfoo  '<
>foo<
$ shopt extglob
extglob         off

7, Autoriteit 4%

U kunt simpelweg trimmen met echo:

foo=" qsdqsd qsdqs q qs   "
# Not trimmed
echo \'$foo\'
# Trim
foo=`echo $foo`
# Trimmed
echo \'$foo\'

8, Autoriteit 2%

Ik heb het altijd gedaan met sed

 var=`hg st -R "$path" | sed -e 's/  *$//'`

Als er een elegante oplossing is, hoop ik dat iemand het pooit.


9, Autoriteit 2%

Met BASH’s Extended Pattern Matching-functies ingeschakeld (shopt -s extglob), kunt u dit gebruiken:

{trimmed##*( )}

om een ​​willekeurige hoeveelheid leidende ruimtes te verwijderen.


10, Autoriteit 2%

U kunt newlines verwijderen met tr:

var=`hg st -R "$path" | tr -d '\n'`
if [ -n $var ]; then
    echo $var
done

11, Autoriteit 2%

# Trim whitespace from both ends of specified parameter
trim () {
    read -rd '' $1 <<<"${!1}"
}
# Unit test for trim()
test_trim () {
    local foo="$1"
    trim foo
    test "$foo" = "$2"
}
test_trim hey hey &&
test_trim '  hey' hey &&
test_trim 'ho  ' ho &&
test_trim 'hey ho' 'hey ho' &&
test_trim '  hey  ho  ' 'hey  ho' &&
test_trim $'\n\n\t hey\n\t ho \t\n' $'hey\n\t ho' &&
test_trim $'\n' '' &&
test_trim '\n' '\n' &&
echo passed

12

Er zijn veel antwoorden, maar ik geloof nog steeds dat mijn Just-geschreven script de moeite waard is om te worden vermeld, omdat:

  • Het is succesvol getest in de Shells Bash / Dash / Seepbox Shell
  • Het is extreem klein
  • Het is niet afhankelijk van externe opdrachten en hoeft niet te vork (- & GT; snel en laag resource-gebruik)
  • Het werkt zoals verwacht:
    • het stript alle spaties en tabbladen vanaf het begin en einde, maar niet meer
    • BELANGRIJK: het verwijdert niets uit het midden van de tekenreeks (veel andere antwoorden), zelfs newlines blijven
    • speciaal: de "$*"voegt meerdere argumenten samen met één spatie. als u wilt trimmen & voer alleen het eerste argument uit, gebruik in plaats daarvan "$1"
    • als er geen problemen zijn met het matchen van bestandsnaampatronen enz.

Het script:

trim() {
  local s2 s="$*"
  until s2="${s#[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  until s2="${s%[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  echo "$s"
}

Gebruik:

mystring="   here     is
    something    "
mystring=$(trim "$mystring")
echo ">$mystring<"

Uitvoer:

>here     is
    something<

Antwoord 13

Als je shopt -s extglobhebt ingeschakeld, dan is het volgende een nette oplossing.

Dit werkte voor mij:

text="   trim my edges    "
trimmed=$text
trimmed=${trimmed##+( )} #Remove longest matching series of spaces from the front
trimmed=${trimmed%%+( )} #Remove longest matching series of spaces from the back
echo "<$trimmed>" #Adding angle braces just to make it easier to confirm that all spaces are removed
#Result
<trim my edges>

Om dat op minder regels te zetten voor hetzelfde resultaat:

text="    trim my edges    "
trimmed=${${text##+( )}%%+( )}

Antwoord 14

# Strip leading and trailing white space (new line inclusive).
trim(){
    [[ "$1" =~ [^[:space:]](.*[^[:space:]])? ]]
    printf "%s" "$BASH_REMATCH"
}

OF

# Strip leading white space (new line inclusive).
ltrim(){
    [[ "$1" =~ [^[:space:]].* ]]
    printf "%s" "$BASH_REMATCH"
}
# Strip trailing white space (new line inclusive).
rtrim(){
    [[ "$1" =~ .*[^[:space:]] ]]
    printf "%s" "$BASH_REMATCH"
}
# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}

OF

# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi
    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

OF

# Strip leading specified characters.  ex: str=$(ltrim "$str" $'\n a')
ltrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi
    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"]) ]]
    printf "%s" "${BASH_REMATCH[1]}"
}
# Strip trailing specified characters.  ex: str=$(rtrim "$str" $'\n a')
rtrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi
    [[ "$1" =~ ^(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}
# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1" "$2")" "$2")"
}

OF

Voortbouwend op de expr-oplossing van moskit…

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)[[:space:]]*$"`"
}

OF

# Strip leading white space (new line inclusive).
ltrim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)"`"
}
# Strip trailing white space (new line inclusive).
rtrim(){
    printf "%s" "`expr "$1" : "^\(.*[^[:space:]]\)[[:space:]]*$"`"
}
# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}

Antwoord 15

Dit is wat ik deed en werkte perfect en zo eenvoudig:

the_string="        test"
the_string=`echo $the_string`
echo "$the_string"

Uitvoer:

test

Antwoord 16

Je kunt ouderwetse trgebruiken. Dit retourneert bijvoorbeeld het aantal gewijzigde bestanden in een git-repository, witruimten gestript.

MYVAR=`git ls-files -m|wc -l|tr -d ' '`

Antwoord 17

Gebruik AWK:

echo $var | awk '{gsub(/^ +| +$/,"")}1'

Antwoord 18

Ik heb gezien dat scripts alleen variabele toewijzing gebruiken om het werk te doen:

$ xyz=`echo -e 'foo \n bar'`
$ echo $xyz
foo bar

Witruimte wordt automatisch samengevoegd en bijgesneden. Men moet voorzichtig zijn met shell-metakarakters (potentieel injectierisico).

Ik zou ook aanraden om variabele vervangingen altijd dubbel te citeren in shell-voorwaarden:

if [ -n "$var" ]; then

aangezien iets als a -o of andere inhoud in de variabele je testargumenten zou kunnen wijzigen.


Antwoord 19

Ik zou gewoon sed gebruiken:

function trim
{
    echo "$1" | sed -n '1h;1!H;${;g;s/^[ \t]*//g;s/[ \t]*$//g;p;}'
}

a) Voorbeeld van gebruik op string met één regel

string='    wordA wordB  wordC   wordD    '
trimmed=$( trim "$string" )
echo "GIVEN STRING: |$string|"
echo "TRIMMED STRING: |$trimmed|"

Uitvoer:

GIVEN STRING: |    wordA wordB  wordC   wordD    |
TRIMMED STRING: |wordA wordB  wordC   wordD|

b) Voorbeeld van gebruik op string met meerdere regels

string='    wordA
   >wordB<
wordC    '
trimmed=$( trim "$string" )
echo -e "GIVEN STRING: |$string|\n"
echo "TRIMMED STRING: |$trimmed|"

Uitvoer:

GIVEN STRING: |    wordAA
   >wordB<
wordC    |
TRIMMED STRING: |wordAA
   >wordB<
wordC|

c) Laatste opmerking:
Als u een functie niet wilt gebruiken, kunt u voor eenregelige tekenreekseenvoudig een “gemakkelijker te onthouden” opdracht gebruiken, zoals:

echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

Voorbeeld:

echo "   wordA wordB wordC   " | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

Uitvoer:

wordA wordB wordC

Het gebruik van het bovenstaande op strings met meerdere regels werkt ook, maar houd er rekening mee dat het ook alle achter-/voorloopinterne meervoudige spatie weghaalt, zoals GuruM opmerkte in de opmerkingen

string='    wordAA
    >four spaces before<
 >one space before<    '
echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

Uitvoer:

wordAA
>four spaces before<
>one space before<

Dus als je het erg vindt om die spaties te behouden, gebruik dan de functie aan het begin van mijn antwoord!

d) UITLEGvan de sed-syntaxis “vinden en vervangen” op strings met meerdere regels die worden gebruikt in de functie trim:

sed -n '
# If the first line, copy the pattern to the hold buffer
1h
# If not the first line, then append the pattern to the hold buffer
1!H
# If the last line then ...
$ {
    # Copy from the hold to the pattern buffer
    g
    # Do the search and replace
    s/^[ \t]*//g
    s/[ \t]*$//g
    # print
    p
}'

Antwoord 20

var='   a b c   '
trimmed=$(echo $var)

Antwoord 21

Hier is een trim()-functie die witruimte bijsnijdt en normaliseert

#!/bin/bash
function trim {
    echo $*
}
echo "'$(trim "  one   two    three  ")'"
# 'one two three'

en een andere variant die reguliere uitdrukkingen gebruikt.

#!/bin/bash
function trim {
    local trimmed="$@"
    if [[ "$trimmed" =~ " *([^ ].*[^ ]) *" ]]
    then 
        trimmed=${BASH_REMATCH[1]}
    fi
    echo "$trimmed"
}
echo "'$(trim "  one   two    three  ")'"
# 'one   two    three'

22

Om spaties en tabbladen van links naar het eerste woord te verwijderen, voert u in:

echo "     This is a test" | sed "s/^[ \t]*//"

cyberciti.biz/tips/delete- Leading-spaces-front-of-eeds-word.html


23

Hiermee wordt alle witruimtes uit uw string verwijderd,

VAR2="${VAR2//[[:space:]]/}"

/Vervangt de eerste voorval en //alle voorvallen van witvakken in de tekenreeks. D.w.z. Alle witte ruimtes worden vervangen door – niets


24

Dit is de eenvoudigste methode die ik heb gezien. Het gebruikt alleen BASH, het is maar een paar regels, het regexp is eenvoudig, en het komt overeen met alle vormen van witruimte:

if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi

Hier is een voorbeeldscript om het te testen met:

test=$(echo -e "\n \t Spaces and tabs and newlines be gone! \t  \n ")
echo "Let's see if this works:"
echo
echo "----------"
echo -e "Testing:${test} :Tested"  # Ugh!
echo "----------"
echo
echo "Ugh!  Let's fix that..."
if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi
echo
echo "----------"
echo -e "Testing:${test}:Tested"  # "Testing:Spaces and tabs and newlines be gone!"
echo "----------"
echo
echo "Ah, much better."

Antwoord 25

Toewijzingen negeren voorloop- en volgspaties en kunnen als zodanig worden gebruikt om bij te snijden:

$ var=`echo '   hello'`; echo $var
hello

Antwoord 26

Dit heeft niet het probleem met ongewenste globbing, ook is de interne witruimte ongewijzigd (ervan uitgaande dat $IFSis ingesteld op de standaardwaarde, namelijk ' \t\n').

Het leest tot aan de eerste nieuwe regel (en bevat deze niet) of het einde van de tekenreeks, wat het eerst komt, en verwijdert elke combinatie van voorloop- en volgspaties en \t-tekens. Als u meerdere regels wilt behouden (en ook voorloop- en volgregels wilt verwijderen), gebruik dan read -r -d '' var << eofin plaats daarvan; houd er echter rekening mee dat als uw invoer toevallig \neofbevat, deze vlak daarvoor wordt afgebroken. (Andere vormen van witruimte, namelijk \r, \fen \v, worden nietverwijderd, zelfs als je ze toevoegt aan $IFS.)

read -r var << eof
$var
eof

Antwoord 27

Python heeft een functie strip()die identiek werkt aan PHP’s trim(), dus we kunnen een beetje inline Python doen om hiervoor een gemakkelijk te begrijpen hulpprogramma te maken :

alias trim='python -c "import sys; sys.stdout.write(sys.stdin.read().strip())"'

Hiermee worden voorloop- en achterliggende witruimte bijgesneden (inclusief nieuwe regels).

$ x=`echo -e "\n\t   \n" | trim`
$ if [ -z "$x" ]; then echo hi; fi
hi

Antwoord 28

Spaties naar één spatie verwijderen:

(text) | fmt -su

Antwoord 29

Ik moest de witruimte van een script inkorten toen de variabele IFSop iets anders was ingesteld. Vertrouwen op Perldeed de truc:

# trim() { echo $1; } # This doesn't seem to work, as it's affected by IFS
trim() { echo "$1" | perl -p -e 's/^\s+|\s+$//g'; }
strings="after --> , <-- before,  <-- both -->  "
OLD_IFS=$IFS
IFS=","
for str in ${strings}; do
  str=$(trim "${str}")
  echo "str= '${str}'"
done
IFS=$OLD_IFS

Other episodes