Как рекурсивно удалить конечные пробелы во всех файлах?

122

Как удалить все завершающие пробелы всего проекта? Начиная с корневого каталога и удаляя конечные пробелы из всех файлов во всех папках.

Кроме того, я хочу иметь возможность напрямую изменять файл, а не просто выводить все на стандартный вывод.

iamjwc
источник
О, вы ищете «портативное» решение или более специфичное для ОС? Какую ОС вы используете?
Джо Пинеда,
3
Мне бы хотелось увидеть такую ​​версию, которая работала бы в OS X Snow Leopard и игнорировала бы папки .git и .svn.
Trevor Turk,

Ответы:

84

Вот решение OS X> = 10.6 Snow Leopard.

Он игнорирует папки .git и .svn и их содержимое. Также он не оставит файл резервной копии.

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:]]*$//"
глубокий колодец
источник
10
Вы можете сделать это быстрее, используя \+вместо *строки замены - в противном случае она будет соответствовать каждой отдельной строке.
l0b0 05
10
Вы можете использовать [[: blank:]] для удаления табуляции и пробелов.
Leif Gruenwoldt
21
В Mountain Lion это возвращается sed: RE error: illegal byte sequenceко мне.
Брайсон
12
Для тех из вас, у кого есть проблемы с «неправильной последовательностью байтов»: введите export LANG=Cи попробуйте еще раз
Георг Ледерманн,
3
В OS X 10.9 мне также понадобилось export LC_CTYPE=C найти здесь: stackoverflow.com/questions/19242275/…
kissgyorgy
31

Использование:

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

если вы не хотите, чтобы файлы ".bak" создавались:

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

как пользователь zsh вы можете опустить вызов find и вместо этого использовать:

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

Примечание: Для того, чтобы предотвратить уничтожение .gitкаталога, попробуйте добавить: -not -iwholename '*.git*'.

Sec
источник
37
Не пытайтесь использовать это в репозитории git, так как это может повредить внутреннее хранилище git.
mgold
11
@mgold Слишком поздно, grrr; /
kenorb
3
Чтобы уточнить, можно запускать это внутри подпапки репозитория git, но не внутри папок, которые содержат репозиторий git в качестве потомков, то есть не внутри папок с .gitкаталогами, независимо от того, насколько глубоко они вложены.
Илья Москвин
Объединение этого ответа с @ deepwell's, чтобы избежать проблем с git / svnfind . -not \( -name .svn -prune -o -name .git -prune \) -type f -print0 | xargs -0 perl -pi -e 's/ +$//'
Уильям Деннисс,
1
Вероятно, есть способ лучше, но я восстановился после искажения репозитория git с этим, клонировав репо в отдельной папке, а затем выполнив это, rsync -rv --exclude=.git repo/ repo2/после чего локальные изменения repoтакже были в (неповрежденном) repo2.
MatrixManAtYrService
29

Два альтернативных подхода, которые также работают с символами новой строки DOS (CR / LF) и позволяют довольно хорошо избегать двоичных файлов :

Общее решение, которое проверяет, что тип MIME начинается с 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)

Решение для репозитория Git от Mat, в котором используется-Iопцияgit grepпропуска файлов, которые Git считает двоичными:

git grep -I --name-only -z -e '' | xargs -0 sed -i 's/[ \t]\+\(\r\?\)$/\1/'
l0b0
источник
3
Так что мне очень нравится это решение git. Он действительно должен быть на высоте. Однако я не хочу сохранять возврат каретки. Но я предпочитаю это тому, что я объединил в 2010 году.
odinho - Velmont
Мой мерзавец жалуется, что выражение -e пустое, но оно отлично работает с -e '. *'
muirbot
@okor В GNU sedопция суффикса -iявляется необязательной , но в BSDsed это не так . Здесь это, строго говоря, и не обязательно, поэтому я просто удалю его.
l0b0
24

В Баше:

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

Примечание: Если вы используете .gitрепозиторий, попробуйте добавить: -not -iwholename '.git'.

Адам Розенфилд
источник
Это генерирует подобные ошибки для каждого найденного файла. sed: 1: "dir / file.txt": команда ожидает \, за которой следует текст
iamjwc
Замена ';' с участием \; должно сработать. (Также кавычки вокруг {} строго не требуются).
agnul
4
Чтобы удалить все пробелы, а не только пробелы, вы должны заменить символ пробела на [: space:] в регулярном выражении sed.
WMR,
Другое примечание: это работает только с версиями sed> = 4, меньшие версии не поддерживают редактирование на месте.
WMR,
1
Это сломало мой мерзавец :(
CrabMan
14

Это сработало для меня в OSX 10.5 Leopard, которая не использует GNU sed или xargs.

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

Просто будьте осторожны с этим, если у вас есть файлы, которые нужно исключить (я сделал)!

Вы можете использовать -prune, чтобы игнорировать определенные каталоги или файлы. Для файлов Python в репозитории git вы можете использовать что-то вроде:

find dir -not -path '.git' -iname '*.py'
POJO
источник
Есть шанс уточнить это? Мне нужна команда, которая будет рекурсивно удалять конечные пробелы из всех файлов в каталоге, игнорируя каталог «.git». Я не могу полностью последовать твоему примеру ...
Тревор Терк
Если вы используете tcsh, вам нужно заменить двойные кавычки на одинарные. В противном случае вы получите «Недопустимое имя переменной». ошибка.
Брэндон Фосдик
GNU sed аналогичен, но вы выполняете -i.bak или --in-place = .bak, в результате чего получаете полную команду find dir -not -path '.git' -iname '*.py' -print0 | xargs -0 sed --in-place=.bak 's/[[:space:]]*$//'. Замените dirкаталог, о котором идет речь, как верхний уровень для рекурсии.
Дэвид Гарднер
sed -i .bak? Разве не должно быть sed -i.bak(без пробела)?
Ondra ižka
9

Ack был создан для такого рода задач.

Он работает так же, как grep, но знает, что нельзя спускаться в такие места, как .svn, .git, .cvs и т. Д.

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

Намного проще, чем прыгать через обруч с помощью find / grep.

Ack доступен через большинство менеджеров пакетов (как ack или ack-grep ).

Это просто программа на Perl, поэтому она также доступна в однофайловой версии, которую вы можете просто загрузить и запустить. См .: Подтверждение установки

jbbuckley
источник
ackэто чудесно. Использовал его в течение нескольких лет и доступен почти во всех репозиториях пакетов для большинства дистрибутивов.
Фелипе Альварес
8

ex

Попробуйте использовать редактор Ex (часть Vim):

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

Примечание. Для рекурсии (bash4 и zsh) мы используем новую опцию подстановки ( **/*.*). Включить shopt -s globstar.

Вы можете добавить в свой .bash_profile:

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

sed

Для использования sedпроверьте: Как удалить конечные пробелы с помощью sed?

find

Найдите следующий скрипт (например remove_trail_spaces.sh) для удаления завершающих пробелов из файлов:

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

Запустите этот сценарий из каталога, который вы хотите просканировать. В OSX в конце он удалит все файлы, заканчивающиеся на .bak.

Или просто:

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

который рекомендуется в Spring Framework Code Style .

kenorb
источник
find . -type f -name "*.java" -exec perl -p -i -e "s/[ \t]$//g" {} \;удаляет только один конечный пробел вместо всех.
Karl Richter
6

В итоге я не использовал поиск и не создавал файлы резервных копий.

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

В зависимости от глубины дерева файлов этой (более короткой версии) может быть достаточно для ваших нужд.

ПРИМЕЧАНИЕ, например, это также относится к двоичным файлам.

Джеспер Рённ-Йенсен
источник
Для конкретных файлов: найти. -name '* .rb' | xargs -I {} sed -i '' 's / [[: space:]] * $ // g' {}
Gautam Rege
Вам не нужен параметр "" для sed; или я могу что-то упустить. Я попробовал это для всех файлов в данном каталоге, например: sed -i 's / [[: space:]] * $ // g' util / *. M
Мирча
6

Вместо исключения файлов, вот вариант вышеупомянутого явно белого списка файлов, основанный на расширении файла, который вы хотите удалить, не стесняйтесь приправлять по вкусу:

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
источник
Чтобы это сработало для меня, мне нужно было добавить цитаты:-name "*.rb*"
haroldcarr
5

В итоге я запустил это, которое представляет собой смесь версий pojo и adams.

Он очистит как завершающие пробелы, так и другую форму конечных пробелов, возврат каретки:

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

Он не коснется папки .git, если она есть.

Edit : Сделано немного безопаснее после комментария, не позволяя принимать файлы с «.git» или «.svn» в нем. Но будьте осторожны, это будет касаться двоичных файлов , если у вас есть некоторые из них . Используйте -iname "*.py" -or -iname "*.php"после, -type fесли хотите, чтобы он касался, например, файлов .py и .php.

Обновление 2 : теперь оно заменяет все виды пробелов в конце строки (что также означает табуляции)

odinho - Велмонт
источник
4
Я не знаю, что происходит, но это полностью испортило мой репозиторий git и испортило мои изображения. НАРОД, БУДЬТЕ ВНИМАТЕЛЬНЫМ, ЧЕМ Я БЫЛ!
mattalxndr
Да, это испортит двоичные файлы. Однако он вообще не должен касаться вашего репозитория git, потому что он пропускает все, что находится в папке .git. Но возможно, только если вы находитесь в той же папке.
odinho - Velmont
4

Это хорошо работает .. add / remove --include для определенных типов файлов:

egrep -rl ' $' --include *.c *  | xargs sed -i 's/\s\+$//g'
Грант Мерфи
источник
4

Рубин:

irb
Dir['lib/**/*.rb'].each{|f| x = File.read(f); File.write(f, x.gsub(/[ \t]+$/,"")) }
грубее
источник
3

Я использую регулярные выражения. 4 шага:

  1. Откройте корневую папку в вашем редакторе (я использую Visual Studio Code).
  2. Коснитесь значка поиска слева и включите режим регулярного выражения.
  3. Введите «+ \ n» в строку поиска и «\ n» в строку замены.
  4. Нажмите «Заменить все».

Это удаляет все конечные пробелы в конце каждой строки во всех файлах. И вы можете исключить некоторые файлы, которые не подходят для этой цели.

roedeercuco
источник
2

1) Многие другие ответы используют -E. Я не уверен, почему, поскольку это недокументированный вариант совместимости с BSD . -rследует использовать вместо этого.

2) Остальные ответы использую -i ''. Это должно быть просто -i(или -i''если предпочтительнее), потому -iчто после него стоит суффикс.

3) Конкретное решение 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]+$//'

Первый регистрирует псевдоним git, в check-whitespaceкотором перечислены файлы с конечными пробелами. Второй бежит sedпо ним.

Я использую только \tвместо этого, [:space:]поскольку обычно не вижу вертикальных вкладок, каналов форм и неразрывных пробелов. Ваш размер может отличаться.

Ондра Жижка
источник
1

Вот что работает для меня (Mac OS X 10.8, GNU sed, установленный 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/' \{} \;

Удалены конечные пробелы, табуляция заменена пробелами, Windows CRLF заменена на Unix \n.

Что интересно, я должен запустить это 3-4 раза, прежде чем все файлы будут исправлены, согласно всем gsedинструкциям по очистке .

yegor256
источник