Comment supprimer les espaces blancs avec sed?

113

J'ai un script shell simple qui supprime les espaces de fin d'un fichier. Existe-t-il un moyen de rendre ce script plus compact (sans créer de fichier temporaire)?

sed 's/[ \t]*$//' $1 > $1__.tmp
cat $1__.tmp > $1
rm $1__.tmp
Viktor
la source
2
Vous pouvez utiliser à la mvplace de catet rm. Pourquoi utilisez-vous catcomme ça de toute façon? Pourquoi ne pas l'utiliser cp?
Suspendu jusqu'à nouvel ordre.
1
J'ai utilisé les connaissances que j'ai apprises grâce à cette question pour créer un script shell permettant de supprimer récursivement les espaces de fin .
David Tuite
1
Votre solution est en fait meilleure lors de l'utilisation de MinGW en raison d'un bogue dans sed sous Windows: stackoverflow.com/questions/14313318/...
Cody Piersall
Notez que l'utilisation de catpour écraser le fichier original plutôt que de mvremplacer réellement les données dans le fichier original (c'est-à-dire que cela ne rompra pas les liens physiques). Utiliser sed -icomme proposé dans de nombreuses solutions ne fera pas cela. IOW, continue de faire ce que tu fais.
William Pursell

Réponses:

157

Vous pouvez utiliser l'option en place -ide sedpour Linux et Unix:

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

Sachez que l'expression supprimera les tderniers sur OSX (vous pouvez utiliser gsedpour éviter ce problème). Il peut également les supprimer sur BSD.

Si vous n'avez pas gsed, voici la syntaxe sed correcte (mais difficile à lire) sous OSX:

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

Trois chaînes entre guillemets simples sont finalement concaténées en un seul argument / expression. Il n'y a pas d'opérateur de concaténation dans bash, vous placez simplement les chaînes l'une après l'autre sans espace entre les deux.

Le $'\t'résout comme un caractère de tabulation littéral dans bash (en utilisant les guillemets ANSI-C ), de sorte que l'onglet est correctement concaténé dans l'expression.

codaddict
la source
1
sed: Not a recognized flag: i
J'obtiens
2
hm. son aussi bogué dans le sens où il supprimera tous les "t" de fin :)
Good Person
2
"sed: Pas un drapeau reconnu: i -" Cela se produit sous OSX. Vous devez ajouter une extension pour le fichier de sauvegarde après -i sur Mac. par exemple: sed -i .bak 's / [\ t] * $ //' $ 1
Aimon Bustardo
1
@GoodPerson Si vous ne plaisantez pas, vous oubliez probablement d'échapper à t:) \test un onglet, pour ceux qui ne le savent peut-être pas déjà.
Sean Allred
2
@SeanAllred ne plaisantait pas: c'est complètement cassé à moins que vous n'utilisiez GNU sed (qui est cassé de tant d'autres manières)
Good Person
59

Au moins sur Mountain Lion, la réponse de Viktor supprimera également le caractère «t» lorsqu'il est à la fin d'une ligne. Ce qui suit résout ce problème:

sed -i '' -e's/[[:space:]]*$//' "$1"
acrollet
la source
1
Mon sed voulait aussi une -Eindication "expressions régulières étendues (modernes)"
Jared Beck
Fonctionne comme un charme sur OS X. Merci beaucoup.
jww
1
La réponse de codaddict a le même problème sur OS X (maintenant macOS). C'est la seule solution sur cette plateforme.
Franklin Yu
@JaredBeck Mine sedsur El Capitan ne l'a pas fait.
Franklin Yu
19

Merci à codaddict d'avoir suggéré l' -ioption.

La commande suivante résout le problème sur Snow Leopard

sed -i '' -e's/[ \t]*$//' "$1"
Viktor
la source
7
Comme le dit @acrollet, vous ne pouvez pas utiliser \tavec sed autre que GNU sed et il est interprété comme une lettre littérale t. La commande ne semble fonctionner que, probablement parce qu'il n'y a pas de tabulation dans l'espace blanc de fin ni un tà la fin d'une phrase dans votre fichier. L'utilisation ''sans spécifier de suffixe de sauvegarde n'est pas recommandée.
Scrutinizer
13

Il est préférable de citer également 1 $:

sed -i.bak 's/[[:blank:]]*$//' "$1"
Scrutinizer
la source
5
var1="\t\t Test String trimming   "
echo $var1
Var2=$(echo "${var1}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo $Var2
Sandip Patel - SM
la source
1
Hé, c'est juste ce dont j'avais besoin! Les autres solutions sed publiées avaient des problèmes d'intégration avec une affectation de variable canalisée (et canalisée et canalisée ...) dans mon script bash, mais la vôtre a fonctionné immédiatement.
Eric L.
4

J'ai un script dans mon .bashrc qui fonctionne sous OSX et Linux (bash uniquement!)

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
}

auquel j'ajoute:

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.
la source
3

Pour ceux qui recherchent l'efficacité (beaucoup de fichiers à traiter, ou de gros fichiers), utiliser l' +opérateur de répétition au lieu de *rend la commande plus de deux fois plus rapide.

Avec GNU sed:

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

J'ai également rapidement évalué autre chose: utiliser [ \t]au lieu d' [[:space:]]accélérer considérablement le processus (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
la source
1

Juste pour le fun:

#!/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
la source
0

Dans le cas spécifique de sed, l' -ioption que d'autres ont déjà mentionnée est de loin la plus simple et la plus saine.

Dans le cas plus général sponge, à partir de la moreutilscollection, fait exactement ce que vous voulez: il vous permet de remplacer un fichier par le résultat de son traitement, d'une manière spécialement conçue pour empêcher l'étape de traitement de trébucher sur elle-même en écrasant le fichier même qu'il travaille sur. Pour citer la spongepage de manuel:

sponge lit l'entrée standard et l'écrit dans le fichier spécifié. Contrairement à une redirection shell, sponge absorbe toutes ses entrées avant d'écrire le fichier de sortie. Cela permet de construire des pipelines qui lisent et écrivent dans le même fichier.

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

Dan Martinez
la source
-1

Pour supprimer uniquement les espaces (dans mon cas, les espaces et les tabulations) des lignes avec au moins un caractère non blanc (de cette façon, les lignes vides en retrait ne sont pas touchées):

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