Jak rekurencyjnie usunąć końcowe białe znaki ze wszystkich plików?

122

Jak usunąć wszystkie końcowe spacje z całego projektu? Rozpoczynając od katalogu głównego i usuwając końcowe białe znaki ze wszystkich plików we wszystkich folderach.

Chciałbym również móc bezpośrednio modyfikować plik, a nie tylko drukować wszystko na standardowe wyjście.

iamjwc
źródło
Och, szukasz rozwiązania „przenośnego” czy bardziej specyficznego dla systemu operacyjnego? Z jakiego systemu operacyjnego korzystasz?
Joe Pineda,
3
Bardzo chciałbym zobaczyć wersję tego, która działałaby w systemie OS X Snow Leopard i ignorowałaby foldery .git i .svn.
Trevor Turk

Odpowiedzi:

83

Oto rozwiązanie dla OS X> = 10.6 Snow Leopard.

Ignoruje foldery .git i .svn oraz ich zawartość. Nie pozostawi też pliku kopii zapasowej.

export LC_CTYPE=C
export LANG=C
find . -not \( -name .svn -prune -o -name .git -prune \) -type f -print0 | xargs -0 sed -i '' -E "s/[[:space:]]*$//"
głęboka studnia
źródło
10
Możesz to przyspieszyć, używając \+zamiast *w ciągu zastępczym - w przeciwnym razie pasuje w każdym wierszu.
l0b0
10
Możesz użyć [[: blank:]], aby usunąć zarówno tabulatory, jak i spacje.
Leif Gruenwoldt
21
W Mountain Lion to sed: RE error: illegal byte sequencedla mnie powraca .
Bryson,
12
Dla tych z Was, którzy mają problemy z „niedozwoloną sekwencją bajtów”: wprowadź export LANG=Ci spróbuj ponownie
Georg Ledermann
3
W OS X 10.9 potrzebowałem również, export LC_CTYPE=C jak znalazłem tutaj: stackoverflow.com/questions/19242275/…
kissgyorgy
31

Posługiwać się:

find . -type f -print0 | xargs -0 perl -pi.bak -e 's/ +$//'

jeśli nie chcesz generować plików „.bak”:

find . -type f -print0 | xargs -0 perl -pi -e 's/ +$//'

jako użytkownik zsh możesz pominąć wywołanie funkcji find i zamiast tego użyć:

perl -pi -e 's/ +$//' **/*

Uwaga: Aby zapobiec niszczeniu .gitkatalogu, spróbuj dodać: -not -iwholename '*.git*'.

Sec
źródło
37
Nie próbuj tego w repozytorium git, ponieważ może to uszkodzić pamięć wewnętrzną git.
mgold
11
@mgold Za późno, grrr; /
kenorb
3
Aby wyjaśnić, w porządku jest to uruchomić w podfolderze repozytorium git, ale nie w folderach, które zawierają repozytoria git jako potomków, tj. Nie w żadnych folderach, które mają .gitkatalogi, bez względu na to, jak głęboko zagnieżdżone.
Illya Moskvin
Połączenie tej odpowiedzi z @ deepwell's w celu uniknięcia problemów z git / svnfind . -not \( -name .svn -prune -o -name .git -prune \) -type f -print0 | xargs -0 perl -pi -e 's/ +$//'
William Denniss
1
Prawdopodobnie jest lepszy sposób, ale odzyskałem zdrowie po zniekształceniu repozytorium git, klonując repozytorium w oddzielnym folderze, a następnie robiąc, rsync -rv --exclude=.git repo/ repo2/po czym lokalne zmiany repobyły również w (nieuszkodzone) repo2.
MatrixManAtYrService
29

Dwa alternatywne podejścia, które działają również z znakami nowej linii DOS (CR / LF) i całkiem nieźle radzą sobie z unikaniem plików binarnych :

Ogólne rozwiązanie sprawdzające, czy typ MIME zaczyna się od text/:

while IFS= read -r -d '' -u 9
do
    if [[ "$(file -bs --mime-type -- "$REPLY")" = text/* ]]
    then
        sed -i 's/[ \t]\+\(\r\?\)$/\1/' -- "$REPLY"
    else
        echo "Skipping $REPLY" >&2
    fi
done 9< <(find . -type f -print0)

Rozwiązanie Matta specyficzne dla repozytorium Git, które wykorzystuje-Iopcjęgit greppomijania plików, które Git uważa za binarne:

git grep -I --name-only -z -e '' | xargs -0 sed -i 's/[ \t]\+\(\r\?\)$/\1/'
l0b0
źródło
3
Więc bardzo podoba mi się to rozwiązanie gita. Powinien być naprawdę na szczycie. Nie chcę jednak oszczędzać powrotów karetki. Ale wolę to od tego, które połączyłem w 2010 roku.
odinho - Velmont
Mój git narzeka, że ​​wyrażenie -e jest puste, ale działa świetnie przy użyciu -e '. *'
muirbot
@okor W GNU sedopcja sufiksu do -ijest opcjonalna , ale w BSDsed nie. Ściśle rzecz biorąc, i tak nie jest to konieczne, więc po prostu je usunę.
l0b0
24

W Bash:

find dir -type f -exec sed -i 's/ *$//' '{}' ';'

Uwaga: Jeśli używasz .gitrepozytorium, warto dodać: -not -iwholename '.git'.

Adam Rosenfield
źródło
To generuje takie błędy dla każdego znalezionego pliku. sed: 1: "dir / file.txt": polecenie a oczekuje \ po którym następuje tekst
iamjwc
Zastąpienie ';' z \; powinno działać. (Również cudzysłowy wokół {} nie są ściśle potrzebne).
agnul
4
Aby usunąć wszystkie białe spacje, a nie tylko spacje, należy zamienić znak spacji na [: spacja:] w wyrażeniu regularnym sed.
WMR
Kolejna uwaga boczna: działa to tylko z wersjami sed> = 4, mniejsze wersje nie obsługują edycji w miejscu.
WMR
1
To złamało mi
dupę
14

To zadziałało dla mnie w OSX 10.5 Leopard, który nie używa GNU sed ani xargs.

find dir -type f -print0 | xargs -0 sed -i.bak -E "s/[[:space:]]*$//"

Po prostu bądź ostrożny, jeśli masz pliki, które muszą zostać wykluczone (tak zrobiłem)!

Możesz użyć -prune, aby zignorować określone katalogi lub pliki. W przypadku plików Pythona w repozytorium git możesz użyć czegoś takiego:

find dir -not -path '.git' -iname '*.py'
pojo
źródło
Jest jakaś szansa, że ​​mógłbyś to wyjaśnić? Chciałbym otrzymać polecenie, które rekurencyjnie usunie końcowe białe znaki ze wszystkich plików w katalogu, ignorując katalog „.git”. Nie mogę do końca naśladować twojego przykładu ...
Trevor Turk,
Jeśli używasz tcsh, musisz zmienić podwójne cudzysłowy na pojedyncze cudzysłowy. W przeciwnym razie otrzymasz „Niedozwolona nazwa zmiennej”. błąd.
Brandon Fosdick
GNU sed jest podobny, ale robisz -i.bak lub --in-place = .bak, kończąc na pełnym poleceniu find dir -not -path '.git' -iname '*.py' -print0 | xargs -0 sed --in-place=.bak 's/[[:space:]]*$//'. Zastąp dirodpowiednim katalogiem jako katalogiem najwyższego poziomu, z którego ma nastąpić rekurencja.
David Gardner,
sed -i .bak? Czy nie powinno być sed -i.bak(bez spacji)?
Ondra Žižka
9

Ack został stworzony do tego rodzaju zadań.

Działa tak jak grep, ale wie, że nie schodzi do miejsc takich jak .svn, .git, .cvs itp.

ack --print0 -l '[ \t]+$' | xargs -0 -n1 perl -pi -e 's/[ \t]+$//'

O wiele łatwiejsze niż przeskakiwanie przez obręcze za pomocą funkcji find / grep.

Ack jest dostępny za pośrednictwem większości menedżerów pakietów (jako Ack lub Ack-Grep ).

To tylko program w Perlu, więc jest również dostępny w wersji jednoplikowej, którą można po prostu pobrać i uruchomić. Zobacz: Ack Install

jbbuckley
źródło
ackjest wspaniałe. Używam go od wielu lat i jest dostępny w prawie wszystkich repozytoriach pakietów dla większości dystrybucji.
Felipe Alvarez
8

ex

Spróbuj użyć edytora Ex (część Vima):

$ ex +'bufdo!%s/\s\+$//e' -cxa **/*.*

Uwaga: w przypadku rekurencji (bash4 i zsh) używamy nowej opcji globbingu ( **/*.*). Włącz przez shopt -s globstar.

Możesz dodać następującą funkcję do swojego .bash_profile:

# Strip trailing whitespaces.
# Usage: trim *.*
# See: https://stackoverflow.com/q/10711051/55075
trim() {
  ex +'bufdo!%s/\s\+$//e' -cxa $*
}

sed

Aby użyć sed, sprawdź: Jak usunąć końcowe spacje za pomocą seda?

find

Znajdź następujący skrypt (np. remove_trail_spaces.sh) Do usuwania końcowych białych znaków z plików:

#!/bin/sh
# Script to remove trailing whitespace of all files recursively
# See: /programming/149057/how-to-remove-trailing-whitespace-of-all-files-recursively

case "$OSTYPE" in
  darwin*) # OSX 10.5 Leopard, which does not use GNU sed or xargs.
    find . -type f -not -iwholename '*.git*' -print0  | xargs -0 sed -i .bak -E "s/[[:space:]]*$//"
    find . -type f -name \*.bak -print0 | xargs -0 rm -v
    ;;
  *)
    find . -type f -not -iwholename '*.git*' -print0 | xargs -0 perl -pi -e 's/ +$//'
esac

Uruchom ten skrypt z katalogu, który chcesz przeskanować. W systemie OSX na końcu usunie wszystkie pliki kończące się na .bak.

Lub tylko:

find . -type f -name "*.java" -exec perl -p -i -e "s/[ \t]$//g" {} \;

co jest zalecane przez Spring Framework Code Style .

kenorb
źródło
find . -type f -name "*.java" -exec perl -p -i -e "s/[ \t]$//g" {} \;usuwa tylko jedną spację na końcu zamiast wszystkich.
Karl Richter
6

Skończyło się na tym, że nie korzystałem z funkcji znajdowania i nie tworzyłem plików kopii zapasowych.

sed -i '' 's/[[:space:]]*$//g' **/*.*

W zależności od głębokości drzewa plików ta (krótsza wersja) może być wystarczająca dla Twoich potrzeb.

UWAGA dotyczy to również, na przykład, plików binarnych.

Jesper Rønn-Jensen
źródło
W przypadku określonych plików: znajdź. -name '* .rb' | xargs -I {} sed -i '' 's / [[: space:]] * $ // g' {}
Gautam Rege
Nie potrzebujesz parametru „” dla seda; albo coś może mi umknąć. Wypróbowałem to na wszystkich plikach w podanym katalogu, na przykład: sed -i 's / [[: space:]] * $ // g' util / *. M
Mircea
6

Zamiast wykluczać pliki, oto odmiana powyższej wyraźnie białej listy plików, w oparciu o rozszerzenie pliku, które chcesz usunąć, możesz doprawić do smaku:

find . \( -name *.rb -or -name *.html -or -name *.js -or -name *.coffee -or \
-name *.css -or -name *.scss -or -name *.erb -or -name *.yml -or -name *.ru \) \
-print0 | xargs -0 sed -i '' -E "s/[[:space:]]*$//"
ChicagoBob
źródło
Aby to zadziałało, musiałem dodać cytaty:-name "*.rb*"
haroldcarr
5

Skończyło się na tym, że uruchomiłem to, co jest mieszanką wersji pojo i adams.

Wyczyści zarówno końcowe białe znaki, jak i inną formę końcowych białych znaków, powrót karetki:

find . -not \( -name .svn -prune -o -name .git -prune \) -type f \
  -exec sed -i 's/[:space:]+$//' \{} \;  \
  -exec sed -i 's/\r\n$/\n/' \{} \;

Nie dotknie folderu .git, jeśli taki istnieje.

Edycja : poprawiono trochę bezpieczeństwa po komentarzu, nie pozwalając na pobieranie plików zawierających „.git” lub „.svn”. Ale uwaga, jeśli masz jakieś , będzie dotykać plików binarnych. Użyj -iname "*.py" -or -iname "*.php"after, -type fjeśli chcesz, aby dotyczyło tylko np. Plików .py i .php.

Aktualizacja 2 : Zastępuje teraz wszelkiego rodzaju spacje na końcu linii (co oznacza również tabulatory)

odinho - Velmont
źródło
4
Nie wiem, co się dzieje, ale to całkowicie rozwarło moje repozytorium git i zepsuło moje obrazy. LUDZIE, BĄDŹ BARDZIEJ OSTROŻNY NIŻ BYŁEM!
mattalxndr
Tak, zrujnuje pliki binarne. Jednak nie powinien w ogóle dotykać twojego repozytorium git, ponieważ pomija wszystko, co znajduje się w folderze .git. Ale może tylko wtedy, gdy jesteś w tym samym folderze.
odinho
4

To działa dobrze ... dodaj / usuń --include dla określonych typów plików:

egrep -rl ' $' --include *.c *  | xargs sed -i 's/\s\+$//g'
Grant Murphy
źródło
4

Rubin:

irb
Dir['lib/**/*.rb'].each{|f| x = File.read(f); File.write(f, x.gsub(/[ \t]+$/,"")) }
grubszy
źródło
3

Używam wyrażeń regularnych. 4 kroki:

  1. Otwórz folder główny w swoim edytorze (używam Visual Studio Code).
  2. Stuknij ikonę Szukaj po lewej stronie i włącz tryb wyrażeń regularnych.
  3. Wpisz „+ \ n” na pasku wyszukiwania i „\ n” na pasku zamiany.
  4. Kliknij „Zamień wszystko”.

Spowoduje to usunięcie wszystkich końcowych spacji na końcu każdego wiersza we wszystkich plikach. Możesz też wykluczyć niektóre pliki, które nie spełniają tej potrzeby.

roedeercuco
źródło
2

1) Wiele innych odpowiedzi używa -E. Nie jestem pewien dlaczego, ponieważ jest to nieudokumentowana opcja zgodności z BSD .-rnależy użyć zamiast tego.

2) Użyj innych odpowiedzi -i ''. To powinno być sprawiedliwe -i(lub -i''jeśli jest to preferowane), ponieważ -ima przyrostek zaraz po.

3) Rozwiązanie specyficzne dla Git:

git config --global alias.check-whitespace \
'git diff-tree --check $(git hash-object -t tree /dev/null) HEAD'

git check-whitespace | grep trailing | cut -d: -f1 | uniq -u -z | xargs -0 sed --in-place -e 's/[ \t]+$//'

Pierwsza z nich rejestruje alias git, check-whitespacektóry zawiera listę plików z końcowymi spacjami. Drugi biegnie sedna nich.

Używam \traczej niż, [:space:]ponieważ zazwyczaj nie widzę pionowych zakładek, kanałów danych i nierozerwalnych spacji. Twój pomiar może się różnić.

Ondra Žižka
źródło
1

To działa dla mnie (Mac OS X 10.8, GNU sed zainstalowany przez Homebrew):

find . -path ./vendor -prune -o \
  \( -name '*.java' -o -name '*.xml' -o -name '*.css' \) \
  -exec gsed -i -E 's/\t/    /' \{} \; \
  -exec gsed -i -E 's/[[:space:]]*$//' \{} \; \
  -exec gsed -i -E 's/\r\n/\n/' \{} \;

Usunięto końcowe spacje, zastąpiono tabulatory spacjami, zastąpiono Windows CRLF systemem Unix \n .

Co ciekawe, muszę uruchamiać to 3-4 razy, zanim wszystkie pliki zostaną naprawione, zgodnie ze wszystkimi gsedinstrukcjami czyszczenia .

yegor256
źródło