Come tagliare gli spazi bianchi da una variabile di Bash?

Ho uno script di shell con questo codice:

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

Ma il codice condizionale viene eseguito sempre, poiché hg st stampa sempre almeno un carattere di nuova riga.

  • C’è un modo semplice per eliminare gli spazi bianchi da $var (come trim() in PHP )?

o

  • C’è un modo standard per affrontare questo problema?

Potrei usare sed o AWK , ma mi piacerebbe pensare che ci sia una soluzione più elegante a questo problema.

Definiamo una variabile contenente spazi iniziali, finali e intermedi:

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

Come rimuovere tutti gli spazi bianchi (denotati da [: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 

Come rimuovere solo gli spazi iniziali principali:

 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 

Come rimuovere solo spazi bianchi finali:

 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 

Come rimuovere entrambi gli spazi iniziali e finali – concatena le sed s:

 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 

In alternativa, se la tua bash lo supporta, puoi sostituire echo -e "${FOO}" | sed ... echo -e "${FOO}" | sed ... con sed ... <<<${FOO} , così (per gli spazi bianchi finali):

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

Una risposta semplice è:

 echo " lol " | xargs 

Xargs farà il taglio per te. È un comando / programma, nessun parametro, restituisce la stringa tagliata, facile come quello!

Nota: questo non rimuove gli spazi interni, quindi "foo bar" rimane lo stesso. NON diventa "foobar" .

Esiste una soluzione che utilizza solo i built-in di Bash chiamati caratteri jolly :

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

Ecco lo stesso avvolto in una funzione:

 trim() { local var="$*" # remove leading whitespace characters var="${var#"${var%%[![:space:]]*}"}" # remove trailing whitespace characters var="${var%"${var##*[![:space:]]}"}" echo -n "$var" } 

Si passa la stringa da ritagliare in forma quotata. per esempio:

 trim " abc " 

Una cosa bella di questa soluzione è che funzionerà con qualsiasi shell compatibile con POSIX.

Riferimento

  • Rimuovi gli spazi bianchi iniziali e finali da una variabile Bash ( origine originale )

Bash ha una funzione chiamata espansione dei parametri , che, tra le altre cose, consente la sostituzione delle stringhe basata su cosiddetti pattern (i pattern assomigliano alle espressioni regolari, ma esistono differenze e limitazioni fondamentali). [La linea originale di Flussence: Bash ha espressioni regolari, ma sono ben nascoste:]

Di seguito viene illustrato come rimuovere tutti gli spazi bianchi (anche dall’interno) da un valore variabile.

 $ var='abc def' $ echo "$var" abc def # Note: flussence's original expression was "${var/ /}", which only replaced the *first* space char., wherever it appeared. $ echo -n "${var//[[:space:]]/}" abcdef 

Puoi tagliare semplicemente con l’ echo :

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

Striscia uno spazio iniziale e uno finale

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

Per esempio:

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

Produzione:

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

Striscia tutti gli spazi iniziali e finali

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

Per esempio:

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

Produzione:

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

Per rimuovere tutti gli spazi dall’inizio e alla fine di una stringa (inclusi i caratteri di fine riga):

 echo $variable | xargs echo -n 

Questo rimuoverà anche gli spazi duplicati:

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

Produce: ‘questa stringa ha molti spazi’

Dalla sezione di Bash Guide su globbing

Per utilizzare un extglob in un’espansione parametro

  #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 

Ecco la stessa funzionalità racchiusa in una funzione (NOTA: è necessario citare la stringa di input passata alla funzione):

 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. } 

Uso:

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

Se modifichiamo la funzione da eseguire in una subshell, non dobbiamo preoccuparci di esaminare l’opzione corrente della shell per extglob, possiamo semplicemente impostarla senza influire sulla shell corrente. Questo semplifica enormemente la funzione. Aggiorno anche i parametri posizionali “sul posto” quindi non ho nemmeno bisogno di una variabile locale

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

così:

 $ 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 

Con le funzionalità di corrispondenza dei pattern estese di Bash abilitate ( shopt -s extglob ), puoi usare questo:

{trimmed##*( )}

rimuovere una quantità arbitraria di spazi iniziali.

Puoi eliminare le nuove righe con tr :

 var=`hg st -R "$path" | tr -d '\n'` if [ -n $var ]; then echo $var done 
 # 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 

L’ho sempre fatto con sed

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

Se c’è una soluzione più elegante, spero che qualcuno la pubblichi.

Ci sono molte risposte, ma continuo a credere che il mio script appena scritto meriti di essere menzionato perché:

  • è stato testato con successo nella shell bash / dash / busybox shell
  • è estremamente piccolo
  • non dipende da comandi esterni e non ha bisogno di fork (-> veloce e basso utilizzo delle risorse)
  • funziona come previsto:
    • rimuove tutti gli spazi e le tabs dall’inizio e alla fine, ma non di più
    • importante: non rimuove nulla dal centro della stringa (molte altre risposte lo fanno), anche le nuove righe rimarranno
    • speciale: "$*" unisce più argomenti usando uno spazio. se vuoi tagliare e mostrare solo il primo argomento, usa "$1"
    • se non ha problemi con la corrispondenza dei modelli di nome del file ecc

Il copione:

 trim() { local s2 s="$*" # note: the brackets in each of the following two lines contain one space # and one tab until s2="${s#[ ]}"; [ "$s2" = "$s" ]; do s="$s2"; done until s2="${s%[ ]}"; [ "$s2" = "$s" ]; do s="$s2"; done echo "$s" } 

Uso:

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

Produzione:

 >here is something< 

Puoi usare la vecchia scuola tr . Ad esempio, questo restituisce il numero di file modificati in un repository git, spazi bianchi spogliati.

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

Questo ha funzionato per me:

 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  

Per dirla su meno righe per lo stesso risultato:

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

Ho visto gli script usare solo l’assegnazione delle variabili per fare il lavoro:

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

Gli spazi bianchi vengono automaticamente coalizzati e tagliati. Bisogna fare attenzione ai metacaratteri della shell (potenziale rischio di iniezione).

Vorrei anche raccomandare sempre la doppia citazione delle sostituzioni variabili nei condizionali della shell:

 if [ -n "$var" ]; then 

poiché qualcosa come un -o o un altro contenuto nella variabile potrebbe modificare gli argomenti del test.

 var=' abc ' trimmed=$(echo $var) 

Vorrei semplicemente usare sed:

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

a) Esempio di utilizzo su stringa a linea singola

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

Produzione:

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

b) Esempio di utilizzo su una stringa multi-linea

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

Produzione:

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

c) Nota finale:
Se non ti piace usare una funzione, per una stringa a riga singola puoi semplicemente usare un comando "più facile da ricordare" come:

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

Esempio:

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

Produzione:

 wordA wordB wordC 

Anche l'uso delle stringhe su più righe funzionerà bene , ma tieni presente che taglierà anche qualsiasi spazio interno multiplo trailing e leader, come GuruM ha notato nei commenti

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

Produzione:

 wordAA >four spaces before< >one space before< 

Quindi se ti preoccupi di mantenere quegli spazi, per favore usa la funzione all'inizio della mia risposta!

d) SPIEGAZIONE della syntax sed "trova e sostituisci" su stringhe multi linea utilizzate all'interno del trim di funzione:

 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 }' 
 # Strip leading and trailing white space (new line inclusive). trim(){ [[ "$1" =~ ^[[:space:]]*(.*[^[:space:]])[[:space:]]*$ ]] printf "%s" "${BASH_REMATCH[1]}" } 

O

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

O

 # 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]}" } 

O

 # 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")" } 

O

Basandosi sull’esprovvisazione di mospr’s expr …

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

O

 # 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")")" } 

Ecco una funzione di trim () che taglia e normalizza gli spazi bianchi

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

E un’altra variante che usa le espressioni regolari.

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

Questo non ha il problema con il globbing indesiderato, inoltre, lo spazio bianco interno non è modificato (assumendo che $IFS sia impostato sul valore predefinito, che è ' \t\n' ).

Legge fino alla prima nuova riga (e non la include) o alla fine della stringa, a seconda di quale viene prima, e rimuove qualsiasi combinazione di spazi iniziali e finali e caratteri \t . Se vuoi preservare più linee (e anche rimuovere le righe iniziali e finali), usa invece read -r -d '' var << eof ; nota, tuttavia, che se il tuo input dovesse contenere \neof , verrà troncato poco prima. (Altre forms di spazio bianco, ovvero \r , \f e \v , non vengono eliminate, anche se le aggiungi a $ IFS.)

 read -r var << eof $var eof 

Use AWK:

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

Assignments ignore leading and trailing whitespace and as such can be used to trim:

 $ var=`echo ' hello'`; echo $var hello 
 #!/bin/bash function trim { typeset trimVar eval trimVar="\${$1}" read trimVar << EOTtrim $trimVar EOTtrim eval $1=\$trimVar } # Note that the parameter to the function is the NAME of the variable to trim, # not the variable contents. However, the contents are trimmed. # Example of use: while read aLine do trim aline echo "[${aline}]" done < info.txt # File info.txt contents: # ------------------------------ # ok hello there $ # another line here $ #and yet another $ # only at the front$ #$ # Output: #[ok hello there] #[another line here] #[and yet another] #[only at the front] #[] 

To remove spaces and tabs from left to first word, enter:

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

cyberciti.biz/tips/delete-leading-spaces-from-front-of-each-word.html

This will remove all the whitespaces from your String,

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

/ replaces the first occurrence and // all occurrences of whitespaces in the string. Ie all white spaces get replaced by – nothing

This trims multiple spaces of the front and end

whatever=${whatever%% *}

whatever=${whatever#* }

I found that I needed to add some code from a messy sdiff output in order to clean it up:

 sdiff -s column1.txt column2.txt | grep -F '<' | cut -f1 -d"<" > c12diff.txt sed -n 1'p' c12diff.txt | sed 's/ *$//g' | tr -d '\n' | tr -d '\t' 

This removes the trailing spaces and other invisible characters.

This is the simplest method I’ve seen. It only uses Bash, it’s only a few lines, the regexp is simple, and it matches all forms of whitespace:

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

Here is a sample script to test it with:

 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." 

I created the following functions. I am not sure how portable printf is, but the beauty of this solution is you can specify exactly what is “white space” by adding more character codes.

  iswhitespace() { n=`printf "%d\n" "'$1'"` if (( $n != "13" )) && (( $n != "10" )) && (( $n != "32" )) && (( $n != "92" )) && (( $n != "110" )) && (( $n != "114" )); then return 0 fi return 1 } trim() { i=0 str="$1" while (( i < ${#1} )) do char=${1:$i:1} iswhitespace "$char" if [ "$?" -eq "0" ]; then str="${str:$i}" i=${#1} fi (( i += 1 )) done i=${#str} while (( i > "0" )) do (( i -= 1 )) char=${str:$i:1} iswhitespace "$char" if [ "$?" -eq "0" ]; then (( i += 1 )) str="${str:0:$i}" i=0 fi done echo "$str" } #Call it like so mystring=`trim "$mystring"`