¿Cómo eliminar los espacios en blanco finales con sed?

113

Tengo un script de shell simple que elimina los espacios en blanco finales de un archivo. ¿Hay alguna forma de hacer que este script sea más compacto (sin crear un archivo temporal)?

sed 's/[ \t]*$//' $1 > $1__.tmp
cat $1__.tmp > $1
rm $1__.tmp
Viktor
fuente
2
Puede usar en mvlugar de caty rm. ¿Por qué estás usando catasí de todos modos? ¿Por qué no usar cp?
Pausado hasta nuevo aviso.
1
Utilicé el conocimiento que aprendí de esta pregunta para crear un script de shell para eliminar recursivamente los espacios en blanco finales .
David Tuite
1
Su solución es realmente mejor cuando usa MinGW debido a un error en sed en Windows: stackoverflow.com/questions/14313318/…
Cody Piersall
Tenga en cuenta que usar catpara sobrescribir el archivo original en lugar de mvreemplazar realmente los datos en el archivo original (es decir, no romperá los vínculos físicos). Usar sed -icomo se propone en muchas soluciones no hará eso. OIA, sigue haciendo lo que estás haciendo.
William Pursell

Respuestas:

157

Puede utilizar la opción en lugar -ide sedpara Linux y Unix:

sed -i 's/[ \t]*$//' "$1"

Tenga en cuenta que la expresión eliminará las pistas finales ten OSX (puede usar gsedpara evitar este problema). También puede eliminarlos en BSD.

Si no tiene gsed, aquí está la sintaxis sed correcta (pero difícil de leer) en OSX:

sed -i '' -E 's/[ '$'\t'']+$//' "$1"

Finalmente, tres cadenas entre comillas simples se concatenan en un solo argumento / expresión. No hay un operador de concatenación en bash, simplemente colocas cadenas una tras otra sin espacios entre ellas.

El se $'\t'resuelve como un carácter de tabulación literal en bash (usando comillas ANSI-C ), por lo que la tabulación está correctamente concatenada en la expresión.

codaddict
fuente
1
Recibo lo siguiente en mi máquina que no puedo actualizar: sed: Not a recognized flag: i
javaPlease42
2
hm. también tiene errores en el sentido de que eliminará todas las "t" finales :)
Good Person
2
"sed: no es una bandera reconocida: i -" Esto sucede en OSX. Debe agregar una extensión para el archivo de respaldo después de -i en Mac. por ejemplo: sed -i .bak 's / [\ t] * $ //' $ 1
Aimon Bustardo
1
@GoodPerson Si no estaba bromeando, es probable que se olvide de escapar de t:) \tes una pestaña, para aquellos que aún no lo saben.
Sean Allred
2
@SeanAllred no estaba bromeando: está completamente roto a menos que esté usando GNU sed (que está roto de muchas otras maneras)
Buena persona
59

Al menos en Mountain Lion, la respuesta de Viktor también eliminará el carácter 't' cuando esté al final de una línea. Lo siguiente corrige ese problema:

sed -i '' -e's/[[:space:]]*$//' "$1"
acrollet
fuente
1
Mi sed también quería una -Eindicación de "expresiones regulares extendidas (modernas)"
Jared Beck
Funciona como un encanto en OS X. Muchas gracias.
jww
1
La respuesta de codaddict tiene el mismo problema en OS X (ahora macOS). Esta es la única solución en esta plataforma.
Franklin Yu
@JaredBeck Mine seden El Capitán no lo hizo.
Franklin Yu
19

Gracias a codaddict por sugerir la -iopción.

El siguiente comando resuelve el problema en Snow Leopard

sed -i '' -e's/[ \t]*$//' "$1"
Viktor
fuente
7
Como dice @acrollet, no se puede usar \tcon sed que no sea GNU sed y se interpreta como una letra literal t. El comando solo parece funcionar, probablemente porque no hay TAB en el espacio en blanco final ni tal final de una oración en su archivo. ''No se recomienda el uso sin especificar un sufijo de respaldo.
Escrutador
13

Es mejor cotizar también $ 1:

sed -i.bak 's/[[:blank:]]*$//' "$1"
Escrutador
fuente
5
var1="\t\t Test String trimming   "
echo $var1
Var2=$(echo "${var1}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo $Var2
Sandip Patel - SM
fuente
1
¡Oye, eso es justo lo que necesitaba! Las otras soluciones sed publicadas tuvieron problemas para integrarse con una asignación de variable canalizada (y canalizada y canalizada ...) en mi script bash, pero la suya funcionó de inmediato.
Eric L.
4

Tengo un script en mi .bashrc que funciona en OSX y Linux (¡solo bash!)

function trim_trailing_space() {
  if [[ $# -eq 0 ]]; then
    echo "$FUNCNAME will trim (in place) trailing spaces in the given file (remove unwanted spaces at end of lines)"
    echo "Usage :"
    echo "$FUNCNAME file"
    return
  fi
  local file=$1
  unamestr=$(uname)
  if [[ $unamestr == 'Darwin' ]]; then
    #specific case for Mac OSX
    sed -E -i ''  's/[[:space:]]*$//' $file
  else
    sed -i  's/[[:space:]]*$//' $file
  fi
}

a lo que agrego:

SRC_FILES_EXTENSIONS="js|ts|cpp|c|h|hpp|php|py|sh|cs|sql|json|ini|xml|conf"

function find_source_files() {
  if [[ $# -eq 0 ]]; then
    echo "$FUNCNAME will list sources files (having extensions $SRC_FILES_EXTENSIONS)"
    echo "Usage :"
    echo "$FUNCNAME folder"
    return
  fi
  local folder=$1

  unamestr=$(uname)
  if [[ $unamestr == 'Darwin' ]]; then
    #specific case for Mac OSX
    find -E $folder -iregex '.*\.('$SRC_FILES_EXTENSIONS')'
  else
    #Rhahhh, lovely
    local extensions_escaped=$(echo $SRC_FILES_EXTENSIONS | sed s/\|/\\\\\|/g)
    #echo "extensions_escaped:$extensions_escaped"
    find $folder -iregex '.*\.\('$extensions_escaped'\)$'
  fi
}

function trim_trailing_space_all_source_files() {
  for f in $(find_source_files .); do trim_trailing_space $f;done
}
Pascal T.
fuente
3

Para aquellos que buscan eficiencia (muchos archivos para procesar o archivos enormes), usar el +operador de repetición en lugar de* hace que el comando sea más del doble de rápido.

Con GNU sed:

sed -Ei 's/[ \t]+$//' "$1"
sed -i 's/[ \t]\+$//' "$1"   # The same without extended regex

También comparé rápidamente algo más: usar en [ \t]lugar de [[:space:]]también acelera significativamente el proceso (GNU sed v4.4):

sed -Ei 's/[ \t]+$//' "$1"

real    0m0,335s
user    0m0,133s
sys 0m0,193s

sed -Ei 's/[[:space:]]+$//' "$1"

real    0m0,838s
user    0m0,630s
sys 0m0,207s

sed -Ei 's/[ \t]*$//' "$1"

real    0m0,882s
user    0m0,657s
sys 0m0,227s

sed -Ei 's/[[:space:]]*$//' "$1"

real    0m1,711s
user    0m1,423s
sys 0m0,283s
yolenoyer
fuente
1

Solo por diversión:

#!/bin/bash

FILE=$1

if [[ -z $FILE ]]; then
   echo "You must pass a filename -- exiting" >&2
   exit 1
fi

if [[ ! -f $FILE ]]; then
   echo "There is not file '$FILE' here -- exiting" >&2
   exit 1
fi

BEFORE=`wc -c "$FILE" | cut --delimiter=' ' --fields=1`

# >>>>>>>>>>
sed -i.bak -e's/[ \t]*$//' "$FILE"
# <<<<<<<<<<

AFTER=`wc -c "$FILE" | cut --delimiter=' ' --fields=1`

if [[ $? != 0 ]]; then
   echo "Some error occurred" >&2
else
   echo "Filtered '$FILE' from $BEFORE characters to $AFTER characters"
fi
David Tonhofer
fuente
0

En el caso concreto de sed, la-i opción que otros ya han mencionado es de lejos la más simple y cuerda.

En el caso más general sponge, de la moreutilscolección, hace exactamente lo que quiere: le permite reemplazar un archivo con el resultado de procesarlo, de una manera específicamente diseñada para evitar que el paso de procesamiento se tropiece al sobrescribir el mismo archivo que está. trabajando en. Para citar la spongepágina del manual:

sponge lee la entrada estándar y la escribe en el archivo especificado. A diferencia de una redirección de shell, la esponja absorbe toda su entrada antes de escribir el archivo de salida. Esto permite construir canalizaciones que leen y escriben en el mismo archivo.

https://joeyh.name/code/moreutils/

Dan Martinez
fuente
-1

Para quitar solo los espacios en blanco (en mi caso, espacios y tabulaciones) de las líneas con al menos un carácter que no sea un espacio en blanco (de esta manera, las líneas vacías con sangría no se tocan):

sed -i -r 's/([^ \t]+)[ \t]+$/\1/' "$file"
phk
fuente