Jak usunąć końcowe spacje za pomocą seda?

113

Mam prosty skrypt powłoki, który usuwa końcowe spacje z pliku. Czy istnieje sposób, aby ten skrypt był bardziej zwarty (bez tworzenia pliku tymczasowego)?

sed 's/[ \t]*$//' $1 > $1__.tmp
cat $1__.tmp > $1
rm $1__.tmp
Viktor
źródło
2
Możesz użyć mvzamiast cati rm. Dlaczego w ogóle tego używasz cat? Dlaczego nie używać cp?
Wstrzymano do odwołania.
1
Wiedzę, którą nauczyłem się z tego pytania, wykorzystałem do stworzenia skryptu powłoki do rekurencyjnego usuwania końcowych białych znaków .
David Tuite
1
Twoje rozwiązanie jest faktycznie lepsze, gdy używasz MinGW z powodu błędu w sedzie w systemie Windows: stackoverflow.com/questions/14313318/ ...
Cody Piersall
Zwróć uwagę, że użycie catdo nadpisania oryginalnego pliku, a nie do mvzastąpienia danych w oryginalnym pliku (tj. Nie spowoduje zerwania twardych linków). Używanie sed -izgodnie z propozycją w wielu rozwiązaniach tego nie zrobi. IOW, po prostu rób to, co robisz.
William Pursell

Odpowiedzi:

157

Można użyć w miejsce opcji -iw sedsystemie Linux i Unix:

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

Pamiętaj, że to wyrażenie usunie końcowe znaki tw systemie OSX (możesz użyć, gsedaby uniknąć tego problemu). Może je również usunąć z BSD.

Jeśli nie masz gsed, oto poprawna (ale trudna do odczytania) składnia sed na OSX:

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

Ostatecznie trzy ciągi w apostrofach zostają połączone w jeden argument / wyrażenie. W bash nie ma operatora konkatenacji, po prostu umieszczasz ciągi jeden po drugim bez spacji między nimi.

W $'\t'postanawia dosłownym charakterze języczka bash (stosując ANSI-C cytowanie ), tak, aby było prawidłowo łączone w wyrażeniu.

codaddict
źródło
1
Na moim komputerze otrzymuję następujące informacje, których nie mogę zaktualizować: sed: Not a recognized flag: i
javaPlease42
2
hm. jest również wadliwy w tym sensie, że usunie wszystkie końcowe „t” :)
Good Person
2
"sed: nierozpoznana flaga: i -" Dzieje się tak w systemie OSX. Musisz dodać rozszerzenie pliku kopii zapasowej po -i na komputerach Mac. np .: sed -i .bak 's / [\ t] * $ //' $ 1
Aimon Bustardo
1
@GoodPerson Jeśli nie żartowałeś, prawdopodobnie zapomnisz uciec z t:) \tto zakładka dla tych, którzy jeszcze nie wiedzą.
Sean Allred
2
@SeanAllred nie żartował: jest całkowicie zepsuty, chyba że używasz GNU sed (który jest zepsuty na wiele innych sposobów)
Good Person
59

Przynajmniej na Mountain Lion, odpowiedź Viktora usunie również znak „t”, gdy znajduje się na końcu linii. Następujące rozwiązania rozwiązują ten problem:

sed -i '' -e's/[[:space:]]*$//' "$1"
acrollet
źródło
1
Mój sed chciał także -Ewskazać „rozszerzone (nowoczesne) wyrażenia regularne”
Jared Beck,
Działa jak urok na OS X. Dziękuję bardzo.
jww
1
Odpowiedź codaddict ma ten sam problem na OS X (teraz macOS). To jedyne rozwiązanie na tej platformie.
Franklin Yu
@JaredBeck Mine sedna El Capitan nie.
Franklin Yu
19

Dzięki codaddict za zasugerowanie -iopcji.

Następujące polecenie rozwiązuje problem w systemie Snow Leopard

sed -i '' -e's/[ \t]*$//' "$1"
Viktor
źródło
7
Jak mówi @acrollet, nie można używać \tseda innego niż GNU sed i jest on interpretowany jako literalna litera t. Wydaje się, że polecenie tylko działa, prawdopodobnie dlatego, że tw Twoim pliku nie ma TAB-a w końcowej spacji ani na końcu zdania. Używanie ''bez określenia przyrostka kopii zapasowej nie jest zalecane.
Scrutinizer
13

Najlepiej też podać 1 dolara:

sed -i.bak 's/[[:blank:]]*$//' "$1"
Scrutinizer
źródło
5
var1="\t\t Test String trimming   "
echo $var1
Var2=$(echo "${var1}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo $Var2
Sandip Patel - SM
źródło
1
Hej, właśnie tego potrzebowałem! Inne opublikowane rozwiązania sed miały problem z integracją z przypisaniem zmiennej potokowej (i potokowej i potokowej ...) w moim skrypcie bash, ale twoje zadziałało po wyjęciu z pudełka.
Eric L.,
4

Mam skrypt w moim .bashrc, który działa pod OSX i Linuxem (tylko 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
}

do którego dodaję:

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.
źródło
3

Dla tych, którzy szukają wydajności (wiele plików do przetworzenia lub ogromne pliki), użycie +operatora powtórzeń zamiast *sprawia, że ​​polecenie jest ponad dwukrotnie szybsze.

Z sedem GNU:

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

Szybko przetestowałem też coś innego: używanie [ \t]zamiast [[:space:]]również znacznie przyspiesza proces (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
źródło
1

Dla żartu:

#!/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
źródło
0

W przypadku określonego w sedThe -iopcja, że inni już wspomniano jest zdecydowanie najprostszy i sanest jeden.

W bardziej ogólnym przypadku, spongez moreutilskolekcji robi dokładnie to, co chcesz: umożliwia zastąpienie pliku wynikiem jego przetwarzania w sposób specjalnie zaprojektowany, aby krok przetwarzania nie potknął się o siebie, nadpisując ten sam plik, pracować nad. Cytując spongestronę podręcznika :

sponge czyta standardowe wejście i zapisuje je w określonym pliku. W przeciwieństwie do przekierowania powłoki, sponge pobiera wszystkie dane wejściowe przed zapisaniem pliku wyjściowego. Pozwala to na konstruowanie potoków, które odczytują i zapisują w tym samym pliku.

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

Dan Martinez
źródło
-1

Aby usunąć tylko białe spacje (w moim przypadku spacje i tabulatory) z wierszy zawierających przynajmniej jeden znak niebędący białymi znakami (w ten sposób puste wcięte wiersze nie są dotykane):

sed -i -r 's/([^ \t]+)[ \t]+$/\1/' "$file"
phk
źródło