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

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.


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"

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"


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


5
var1="\t\t Test String trimming   "
echo $var1
Var2=$(echo "${var1}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo $Var2

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
}

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

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

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/


-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"
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.