Cómo recortar el espacio en blanco de una variable Bash?

Tengo un script de shell con este código:

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

Pero el código condicional siempre se ejecuta, porque hg st siempre imprime al menos un carácter de nueva línea.

  • ¿Hay una forma simple de quitar espacio en blanco de $var (como trim() en PHP )?

o

  • ¿Hay una forma estándar de lidiar con este problema?

Podría usar sed o AWK , pero me gustaría pensar que hay una solución más elegante para este problema.

Definamos una variable que contenga espacios en blanco iniciales, finales e intermedios:

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

Cómo eliminar todos los espacios en blanco (denotado por [:space:] en 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 

Cómo eliminar espacios en blanco iniciales solamente:

 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 

Cómo eliminar espacios en blanco finales solo:

 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 

Cómo eliminar los espacios iniciales y finales – encadenar los sed :

 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 

Alternativamente, si su bash lo admite, puede reemplazar echo -e "${FOO}" | sed ... echo -e "${FOO}" | sed ... con sed ... <<<${FOO} , como tal (para espacios en blanco al final):

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

Una respuesta simple es:

 echo " lol " | xargs 

Xargs hará los ajustes para ti. Es un comando / progtwig, sin parámetros, devuelve la cadena recortada, ¡así de fácil!

Nota: esto no elimina los espacios internos por lo que "foo bar" permanece igual. NO se convierte en "foobar" .

Hay una solución que solo usa Bash incorporados llamados comodines :

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

Aquí está lo mismo envuelto en una función:

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

Pasa la cuerda para recortarla en forma de cita. p.ej:

 trim " abc " 

Algo bueno de esta solución es que funcionará con cualquier shell que cumpla con POSIX.

Referencia

  • Eliminar espacios en blanco iniciales y finales de una variable Bash ( fuente original )

Bash tiene una característica llamada expansión de parámetros , que, entre otras cosas, permite el reemplazo de cadenas en base a los llamados patrones (los patrones se parecen a las expresiones regulares, pero existen diferencias y limitaciones fundamentales). [La línea original de flussence: Bash tiene expresiones regulares, pero están bien escondidas:]

A continuación se muestra cómo eliminar todo el espacio en blanco (incluso desde el interior) de un valor de variable.

 $ 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 

Puedes recortar simplemente con echo :

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

Pele un espacio de entrada y uno de salida

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

Por ejemplo:

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

Salida:

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

Pelar todos los espacios iniciales y finales

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

Por ejemplo:

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

Salida:

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

Para eliminar todos los espacios del principio y el final de una cadena (incluidos los caracteres de fin de línea):

 echo $variable | xargs echo -n 

Esto eliminará espacios duplicados también:

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

Produce: ‘esta cadena tiene muchos espacios’

Desde la sección Bash Guide en globbing

Para usar un extglob en una expansión de parámetro

  #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 

Aquí está la misma funcionalidad envuelta en una función (NOTA: necesita citar la cadena de entrada pasada a la función):

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

Si alteramos la función para que se ejecute en una subcadena, no tenemos que preocuparnos de examinar la opción de shell actual para extglob, podemos simplemente configurarla sin afectar el shell actual. Esto simplifica enormemente la función. También actualizo los parámetros posicionales “en su lugar”, así que ni siquiera necesito una variable local

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

asi que:

 $ 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 las funciones de coincidencia de patrones ampliados de Bash habilitadas ( shopt -s extglob ), puede usar esto:

{trimmed##*( )}

para eliminar una cantidad arbitraria de espacios iniciales.

Puede eliminar nuevas líneas 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 

Siempre lo hice con sed

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

Si hay una solución más elegante, espero que alguien la publique.

Hay muchas respuestas, pero sigo creyendo que vale la pena mencionar mi guión recién escrito porque:

  • fue probado con éxito en el caparazón bash / dash / busybox de shells
  • es extremadamente pequeño
  • no depende de comandos externos y no necesita tenedor (-> uso rápido y bajo de recursos)
  • funciona como se esperaba
    • elimina todos los espacios y tabs de principio a fin, pero no más
    • importante: no elimina nada del medio de la cadena (muchas otras respuestas sí), incluso las nuevas líneas permanecerán
    • especial: el "$*" une múltiples argumentos usando un espacio. si desea recortar y generar solo el primer argumento, use "$1" lugar
    • si no tiene ningún problema con los patrones de nombre de archivo coincidentes, etc.

La secuencia de comandos:

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

Salida:

 >here is something< 

Puedes usar el tr vieja escuela. Por ejemplo, esto devuelve la cantidad de archivos modificados en un repository de git, espacios en blanco despojados.

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

Esto funcionó para mí:

 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  

Para poner eso en menos líneas para el mismo resultado:

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

He visto guiones simplemente usar la asignación de variables para hacer el trabajo:

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

El espacio en blanco se fusiona y recorta automáticamente. Hay que tener cuidado con los metacaracteres del caparazón (riesgo potencial de inyección).

También recomendaría siempre las sustituciones de variables de cotización doble en condicionales de shell:

 if [ -n "$var" ]; then 

ya que algo así como un -o u otro contenido en la variable podría modificar sus argumentos de prueba.

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

Simplemente usaría sed:

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

a) Ejemplo de uso en una cadena de línea única

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

Salida:

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

b) Ejemplo de uso en cadenas de varias líneas

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

Salida:

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

c) Nota final:
Si no desea utilizar una función, para una cadena de una sola línea simplemente puede usar un comando "más fácil de recordar" como:

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

Ejemplo:

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

Salida:

 wordA wordB wordC 

Usar lo anterior en cadenas de varias líneas también funcionará , pero tenga en cuenta que también reducirá cualquier espacio múltiple interno o posterior, como lo notó GuruM en los comentarios.

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

Salida:

 wordAA >four spaces before< >one space before< 

Entonces, si te importa guardar esos espacios, usa la función al principio de mi respuesta.

d) EXPLICACIÓN de la syntax sed "buscar y reemplazar" en cadenas de varias líneas utilizadas dentro del ajuste de la función:

 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

Basándose en la exlusión de moskit …

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

Aquí hay una función de recorte () que recorta y normaliza el espacio en blanco

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

Y otra variante que usa expresiones regulares.

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

Esto no tiene el problema de globbing no deseado, también, el espacio en blanco interior no se modifica (suponiendo que $IFS se establece en el valor predeterminado, que es ' \t\n' ).

Lee hasta la primera nueva línea (y no la incluye) o el final de la cadena, lo que ocurra primero, y elimina cualquier combinación de espacio inicial y final y caracteres \t . Si desea conservar varias líneas (y también eliminar nuevas líneas finales y finales), utilice read -r -d '' var << eof en read -r -d '' var << eof lugar; tenga en cuenta, sin embargo, que si su entrada contiene \neof , se cortará justo antes. (Otras formas de espacio en blanco, a saber, \r , \f y \v , no se eliminan, incluso si las agrega 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"`