Wie entferne ich nachgestellte Leerzeichen mit sed?

113

Ich habe ein einfaches Shell-Skript, das nachgestellte Leerzeichen aus einer Datei entfernt. Gibt es eine Möglichkeit, dieses Skript kompakter zu gestalten (ohne eine temporäre Datei zu erstellen)?

sed 's/[ \t]*$//' $1 > $1__.tmp
cat $1__.tmp > $1
rm $1__.tmp
Viktor
quelle
2
Sie können mvanstelle von catund verwenden rm. Warum benutzt du das überhaupt catso? Warum nicht verwenden cp?
Bis auf weiteres angehalten.
1
Ich habe das Wissen, das ich aus dieser Frage gelernt habe, verwendet, um ein Shell-Skript zum rekursiven Entfernen von nachgestellten Leerzeichen zu erstellen .
David Tuite
1
Ihre Lösung ist tatsächlich besser, wenn Sie MinGW aufgrund eines Fehlers in sed unter Windows verwenden: stackoverflow.com/questions/14313318/…
Cody Piersall
Beachten Sie, dass die Verwendung catzum Überschreiben der Originaldatei mvdie Daten in der Originaldatei ersetzt, anstatt sie tatsächlich zu ersetzen (dh, es werden keine festen Links unterbrochen). Die Verwendung, sed -iwie in vielen Lösungen vorgeschlagen, reicht nicht aus. IOW, mach einfach weiter, was du tust.
William Pursell

Antworten:

157

Sie können die anstelle Option -ivon sedfür Linux und Unix:

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

Beachten Sie, dass der Ausdruck unter tOSX nachgestellte Zeichen löscht (Sie können gseddieses Problem vermeiden). Es kann sie auch auf BSD löschen.

Wenn Sie nicht gsed haben, finden Sie hier die korrekte (aber schwer zu lesende) sed-Syntax unter OSX:

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

Drei Zeichenfolgen in einfachen Anführungszeichen werden schließlich zu einem einzigen Argument / Ausdruck verkettet. Es gibt keinen Verkettungsoperator in bash. Sie platzieren einfach Zeichenfolgen nacheinander ohne Leerzeichen dazwischen.

Das wird $'\t'in Bash als wörtliches Tabulatorzeichen aufgelöst (unter Verwendung von ANSI-C-Anführungszeichen ), sodass der Tabulator korrekt mit dem Ausdruck verknüpft wird.

Codaddict
quelle
1
Ich bekomme Folgendes auf meinem Computer, das ich nicht aktualisieren kann: sed: Not a recognized flag: i
javaPlease42
2
Hm. Es ist auch fehlerhaft in dem Sinne, dass es alle nachfolgenden "t" s entfernt :)
Gute Person
2
"sed: Kein erkanntes Flag: i -" Dies geschieht unter OSX. Sie müssen eine Erweiterung für die Sicherungsdatei nach -i auf Macs hinzufügen. zB: sed -i .bak 's / [\ t] * $ //' $ 1
Aimon Bustardo
1
@GoodPerson Wenn Sie nicht scherzen, vergessen Sie wahrscheinlich, dem zu entkommen t:) \tist eine Registerkarte für diejenigen, die es vielleicht noch nicht wissen.
Sean Allred
2
@ SeanAllred war kein Scherz: Es ist völlig kaputt, es sei denn, Sie verwenden zufällig GNU sed (das auf so viele andere Arten kaputt ist)
Gute Person
59

Zumindest bei Mountain Lion entfernt Viktors Antwort auch das Zeichen 't', wenn es sich am Ende einer Zeile befindet. Das folgende Problem wird behoben:

sed -i '' -e's/[[:space:]]*$//' "$1"
Acrollet
quelle
1
Mein Sed wollte auch einen -EHinweis auf "erweiterte (moderne) reguläre Ausdrücke"
Jared Beck
Funktioniert wie ein Zauber unter OS X. Vielen Dank.
JWW
1
Die Antwort von codaddict hat unter OS X (jetzt macOS) das gleiche Problem. Dies ist die einzige Lösung auf dieser Plattform.
Franklin Yu
@ JaredBeck Mine sedauf El Capitan nicht.
Franklin Yu
19

Vielen Dank an codaddict für den Vorschlag der -iOption.

Der folgende Befehl löst das Problem bei Snow Leopard

sed -i '' -e's/[ \t]*$//' "$1"
Viktor
quelle
7
Wie @acrollet sagt, können Sie nicht \tmit sed außer GNU sed verwenden und es wird als wörtlicher Buchstabe interpretiert t. Der Befehl scheint nur zu funktionieren, wahrscheinlich weil sich keine TABs im nachfolgenden Leerzeichen oder ein tam Ende eines Satzes in Ihrer Datei befinden. Die Verwendung ''ohne Angabe eines Sicherungssuffix wird nicht empfohlen.
Scrutinizer
13

Es ist am besten, auch $ 1 zu zitieren:

sed -i.bak 's/[[:blank:]]*$//' "$1"
Scrutinizer
quelle
5
var1="\t\t Test String trimming   "
echo $var1
Var2=$(echo "${var1}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo $Var2
Sandip Patel - SM
quelle
1
Hey, genau das habe ich gebraucht! Die anderen veröffentlichten Sed-Lösungen hatten Probleme bei der Integration mit einer Pipeline-Variablenzuweisung (und Piped- und Piped-Variablenvariablen ...) in meinem Bash-Skript, aber Ihre hat sofort funktioniert.
Eric L.
4

Ich habe ein Skript in meiner .bashrc, das unter OSX und Linux funktioniert (nur 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
}

zu dem ich hinzufüge:

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.
quelle
3

Für diejenigen, die nach Effizienz suchen (viele zu verarbeitende Dateien oder große Dateien), macht die Verwendung des +Wiederholungsoperators *den Befehl mehr als zweimal schneller.

Mit GNU sed:

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

Ich habe auch schnell etwas anderes verglichen : Verwenden [ \t]statt stattdessen [[:space:]]den Prozess erheblich zu beschleunigen (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
quelle
1

Nur zum Spaß:

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

Im konkreten Fall von sedist die -iOption, die andere bereits erwähnt haben, bei weitem die einfachste und vernünftigste.

Im allgemeineren Fall macht spongedie moreutilsSammlung genau das, was Sie wollen: Sie können eine Datei durch das Ergebnis der Verarbeitung ersetzen, und zwar auf eine Weise, die speziell dafür ausgelegt ist, dass der Verarbeitungsschritt nicht über sich selbst stolpert, indem genau die Datei überschrieben wird, um die es sich handelt arbeiten an. Um die spongeManpage zu zitieren :

Schwamm liest Standardeingaben und schreibt sie in die angegebene Datei. Im Gegensatz zu einer Shell-Umleitung nimmt der Schwamm alle Eingaben auf, bevor er die Ausgabedatei schreibt. Dies ermöglicht das Erstellen von Pipelines, die aus derselben Datei lesen und in diese schreiben.

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

Dan Martinez
quelle
-1

So entfernen Sie nur Leerzeichen (in meinem Fall Leerzeichen und Tabulatoren) von Zeilen mit mindestens einem Nicht-Leerzeichen (auf diese Weise werden leere eingerückte Zeilen nicht berührt):

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