Как преобразовать табуляции в пробелы в каждом файле каталога?

Как преобразовать табуляции в пробелы в каждом файле каталога (возможно, рекурсивно)?

Кроме того, есть ли способ установить количество пробелов на вкладку?


person cnd    schedule 19.06.2012    source источник
comment
Вы хотите заменить табуляцию в файлах или именах файлов?   -  person cppcoder    schedule 19.06.2012
comment
pr - замечательная утилита для этого. См. Этот ответ.   -  person codeforester    schedule 09.06.2017
comment
Не рекомендуется заменять табуляции пробелами, так как это повредит другим пользователям, работающим с теми же файлами. Вместо этого просто настройте инструменты на желаемую ширину вкладки.   -  person Diogo Eichert    schedule 16.01.2021


Ответы (19)


Предупреждение: это нарушит ваше репо.

Это повредит двоичные файлы, в том числе под svn, .git! Прочтите комментарии перед использованием!

find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +

Исходный файл сохраняется как [filename].orig.

Замените '* .java' окончанием файла того типа, который вы ищете. Таким образом вы можете предотвратить случайное повреждение двоичных файлов.

Минусы:

  • Заменяет вкладки везде в файле.
  • Это займет много времени, если в этом каталоге окажется SQL-дамп размером 5 ГБ.
person Martin Beckett    schedule 19.06.2012
comment
для визуального пространства, состоящего из табуляции и пробелов, этот подход дает неправильное расширение. - person pizza; 19.06.2012
comment
Я бы также добавил сопоставитель файлов, например, для поиска только файлов .php ./ -iname * .php -type f -exec sed -i 's / \ t / / g' {} \; - person Daniel Luca CleanUnicorn; 26.03.2013
comment
НЕ ИСПОЛЬЗУЙТЕ САД! Если в строке есть встроенная вкладка, вы можете исказить свой код. Это то, для чего предназначена команда expand. Используйте 1_. - person David W.; 12.11.2013
comment
@DavidW. Я бы просто обновил эту команду, чтобы заменить табуляции только с начала строки. find ./ -type f -exec sed -i 's/^\t/####/g' {} \;. Но я не знал о команде расширения - очень полезно! - person Martin Konecny; 07.05.2014
comment
Команда ответа только что уничтожила мой локальный репозиторий git. YMMV. - person Martin T.; 17.06.2014
comment
НЕ ИСПОЛЬЗУЙТЕ! Этот ответ также только что разрушил мой локальный репозиторий git. Если у вас есть файлы, содержащие смешанные табуляции и пробелы, он будет вставлять последовательности #. Вместо этого используйте ответ Джина или комментарий Дожа ниже. - person puppet; 18.08.2014
comment
Я не знаю, почему это убило ваш локальный репозиторий, это не помогло мне. Символы #, возможно, потребуется заменить фактическими пробелами, я предполагаю, что это означает пробелы # в ответе. Но ^ не помогает: вы заменяете только первую вкладку, последующие вкладки не будут заменены, т.е. бесполезны! - person Sander Verhagen; 06.11.2014
comment
Очевидно, что количество места, на которое расширяется вкладка, зависит от контекста. Таким образом, sed - совершенно неподходящий инструмент для этой задачи. - person Sven; 30.03.2015
comment
Да не использовать. Это испортило мои файлы. Используйте команду Джина. - person user1097111; 16.03.2016
comment
См. Мой ответ ниже, чтобы узнать о более безопасной альтернативе. stackoverflow.com/a/41609013/1924979 - person Harsh Vakharia; 12.01.2017
comment
-1, потому что (как указал @Sven) он, по-видимому, игнорирует размер вкладки, что означает, что это может работать правильно только при случайном совпадении желаемого размера вкладки и испортит отступ (и любой другой вид позиционирования с вкладками) во всех остальных случаях. - person Sz.; 14.03.2018
comment
Это довольно плохой ответ. Вероятно, его следует удалить. - person Dakkaron; 05.12.2019
comment
Люди Иисуса Христа, этот ответ превратился в полную бессвязную неразбериху. Предупреждения о том, что он нарушает репо, теперь все ложные - они остались позади, когда команда ответа find не отфильтровала по расширению файла и, таким образом, поразила каждый файл без разбора. - person mtraceur; 26.02.2021

Простая замена на sed - это нормально, но не лучшее решение. Если между вкладками есть «лишние» пробелы, они останутся там после замены, поэтому поля будут неровными. Вкладки, развернутые посередине строк, также будут работать некорректно. Вместо bash мы можем сказать

find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;

применить expand к каждому файлу Java в текущем дереве каталогов. Удалите / замените аргумент -name, если вы ориентируетесь на другие типы файлов. Как упоминается в одном из комментариев, будьте очень осторожны при удалении -name или использовании слабых подстановочных знаков. Вы можете легко убрать репозиторий и другие скрытые файлы без намерения. Вот почему исходный ответ включал следующее:

Вы всегда должны делать резервную копию дерева, прежде чем пробовать что-то подобное, на случай, если что-то пойдет не так.

person Gene    schedule 19.06.2012
comment
Может ли кто-нибудь объяснить, почему использовать _ в команде, а не опускать его и использовать $ 0? - person Jeffrey Martinez; 26.11.2013
comment
@JeffreyMartinez Отличный вопрос. gniourf_gniourf отредактировал мой исходный ответ 11 ноября и сделал пренебрежительные замечания по поводу незнания правильного способа использования {}. Похоже, он не знал о $0, когда используется -c. Затем dimo414 изменил использование временного интервала в каталоге преобразования на /tmp, что будет намного медленнее, если /tmp находится в другой точке монтирования. К сожалению, у меня нет Linux, чтобы проверить ваше $0 предложение. Но я думаю, что вы правы. - person Gene; 26.11.2013
comment
@Gene, спасибо за разъяснения, это звучит как stackoverflow: p. Хотя, пока я нахожусь на этом, я добавлю, что мне пришлось использовать кавычки вокруг '* .java' для правильного экранирования * .java. - person Jeffrey Martinez; 26.11.2013
comment
@JeffreyMartinez @Gene Don't omit the _ and try to use $0 inside the mini-script -- not only would that be more confusing, but it is also prone to failure if the filename provided by find has special meaning as an argument to the shell. mywiki.wooledge.org/UsingFind - person sabgenton; 30.11.2013
comment
Позиционный параметр $ 0 действительно предназначался для расширения до имени имени программы, если вы опустите все аргументы, он фактически расширяется до bash в этом сценарии. - person sabgenton; 30.11.2013
comment
@sabgenton Но согласно документации bash для параметра -c: если Bash запускается с параметром -c, тогда $ 0 устанавливается в качестве первого аргумента после строки, которая должна быть выполнена, если таковой имеется. Похоже, они действительно хотели сделать первый аргумент доступным как $0. - person Gene; 30.11.2013
comment
Очевидно, что позволяет опция -c, но, похоже, есть много хакеров, которые не рекомендуют использовать ее таким образом в данном контексте. Я не эксперт, но полагаю, что $ 1 будет пустым, если не заданы аргументы, тогда как $ 0 расширится до 'bash', что не то, что вы хотите. gniourf_gniourf не был сбит с толку или что-то в этом роде, он следовал соглашению, я не вижу причин оспаривать это. - person sabgenton; 01.12.2013
comment
@sabgenton, я не понимаю, что вы имеете в виду ... также склонны к сбою, если имя файла ... имеет особое значение ... для оболочки. Мне не хватает части о том, как это может вызвать проблемы, независимо от того, используем ли мы '_' или нет. Не могли бы вы прояснить этот момент? Я понимаю, что это может быть соглашение, но всякий раз, когда я слышу соглашение, меня утомляет то, что это может быть код, потому что это делают все остальные: p - person Jeffrey Martinez; 03.12.2013
comment
@JeffreyMartinez Парень, стоящий за этим сайтом, пользуется большим уважением в IRC-сообществе bash (больше, чем кто-либо из тех, кого я видел). Но я не эксперт, и все, что я могу вам сказать, это то, что обычно 0 долларов - это название программы. Запустите bash -c 'echo "$0"' без аргументов, и вы увидите, что 0 - это имя программы 'bash'. Если ваш аргумент ничего не вернул, вы все равно получите строку 'bash' в программе, зачем вам это? - person sabgenton; 03.12.2013
comment
Вы правы насчет того, что $ 0 - это «тупица», если у меня нет аргументов. Но здесь используется 'find', который всегда предоставляет аргумент (имя файла), которому, если есть аргумент, присваивается $ 0, а не 'bash'. Вот что я вижу прямо со своего терминала: $ bash -c 'echo | $ 0 | $ 1 |' ---- ›| bash || ...... $ bash -c 'echo | $ 0 | $ 1 |' один ---- ›| один || ....... $ bash -c 'echo | $ 0 | $ 1 |' один два ------ ›| один | два | - person Jeffrey Martinez; 04.12.2013
comment
Кроме того, и я признаю, что искал почти 2 минуты, но я не могу найти, где задокументировано, что bash будет отображаться в $ 0, когда не переданы аргументы, тогда как очень четко задокументировано, что вы можете ожидать первого аргумент после строки, которая должна появиться в $ 0. Я также не согласен с тем, что "_" более читабельно, поскольку, если вы еще не знаете его намерение (потому что гуру однажды сказал вам), невозможно определить, для чего он нужен. - person Jeffrey Martinez; 04.12.2013
comment
Справедливый вызов, хотя люди привыкли к написанию сценариев оболочки с использованием ./somefile bla, а при использовании файла для сценариев bla составляет 1 доллар. Это норма :) - person sabgenton; 05.12.2013
comment
Предупреждение для пользователей Windows: развернуть означает совсем другое. - person tomByrer; 07.12.2013
comment
Если у кого-то возникла ошибка "неизвестный первичный или оператор" из find, то вот полная команда, которая исправит это: find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \; - person Doge; 04.04.2014
comment
@Micro Спасибо. Я сделал исходный пост, который работал, но люди продолжают его редактировать, ломая по-разному. Спасибо, что исправили (еще раз). - person Gene; 05.04.2014
comment
есть идеи, как это сделать на машине с Windows с помощью git-bash / msysgit? У меня работает большинство команд Linux. Получение отсутствующего аргумента для '-exec' на этом - person isimmons; 13.04.2014
comment
@isimmons Это настройка, которую я использовал для тестирования в Windows. У меня отлично работает. bash на вашем пути? Если нет, вы можете попробовать \bin\bash вместо bash в командной строке. - person Gene; 16.04.2014
comment
@Gene да, Баш на пути. Я могу набрать bash и получить bash-3.1 $ Но я использовал cmder. Когда я попробовал это с помощью git-bash, он работает. Возможно, когда он не запускается через git-bash, он пытается использовать команду расширения Windows или проблема с одинарными и двойными кавычками. Не знаю, но это работает в git-bash. Спасибо - person isimmons; 20.04.2014
comment
@isimmons Как говорится в сообщении, это команда bash. Это будет иметь смысл только для оболочки bash. - person Gene; 21.04.2014
comment
У меня снова возникли проблемы из-за некоторых изменений в моей системной PATH, которые заставили меня узнать в cmder, что я могу ввести 'sh', который переводит меня в приглашение оболочки через msysgit / bin / sh.exe, а затем эта команда работает отлично. Лучше, чем открывать git-bash для его запуска. - person isimmons; 05.07.2014
comment
Я думал, что в этом ответе недостаточно комментариев, так что это мой: если использовать, используйте sponge из joeyh. имя / код / ​​moreutils, можно написать find . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \; - person tokland; 09.10.2014
comment
Не будь дураком и используйте find . -name '*', я только что уничтожил свой локальный репозиторий git - person Gautam; 22.03.2015
comment
Спасибо, я использовал это для раскрытия: найти. -name * .js -exec bash -c 'unepand -t 4 --first-only $ 0 ›/ tmp / totabbuff && mv / tmp / totabbuff $ 0' {} \; - person arkod; 04.11.2015
comment
Это больше не работает. find: отсутствует аргумент для `-exec ' - person SmallChess; 08.07.2016
comment
@Gene извините за комментарий, но ответ, который вы опубликовали на мой вопрос, был очень хорошим. Я отредактировал вопрос только потому, что у меня возникли проблемы с точным выражением того, что я хотел, но ваш ответ определенно был тем, что я искал. - person MaiaVictor; 29.08.2016
comment
Обратите внимание: это заменит символические ссылки на фактические файлы. Замените ! -type d на -type f, если вы этого не хотите. - person orestisf; 12.01.2017
comment
Работал для меня со следующими оговорками; 1) На Mac нужно было использовать -type d @orestisf и 2) работал только с подстановочным знаком имени, а не в кавычках. - person Syntax; 20.02.2017
comment
Это моя версия: find . -name '*.js' ! -type d -exec bash -c 'expand -t 2 "$0" > /tmp/e && mv /tmp/e "$0"' {} \; - person Pencilcheck; 27.06.2017
comment
Я добавил alias tabs_to_spaces="echo 'this-url'; echo \"find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "'\"\$0\"'" > /tmp/e && mv /tmp/e "'\"\$0\"'"' {} \\;\"", чтобы напомнить мне. Он выводит команду для запуска стиля C-c C-v. - person Karl; 17.01.2018
comment
Я часто использую его, чтобы избежать скрытых папок типа .git: find . -not -path '*/.git/*' -name '*.cs' -type f -exec bash -c 'expand -i -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \; - person mja; 09.04.2018
comment
Это изменяет права доступа к файлам. Я исправил это с помощью chmod --reference: stackoverflow.com/a/52136507/895245 - person Ciro Santilli 新疆再教育营六四事件ۍ 02.09.2018
comment
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功 Это не меняет разрешения. Он создает новые файлы с любыми разрешениями по умолчанию (umask), обычно установленными в bashrc. - person Gene; 02.09.2018
comment
@Gene да, и поэтому он иногда меняет права доступа к файлам, что, вероятно, не то, чего хотят люди, особенно для исполняемых файлов сценариев. - person Ciro Santilli 新疆再教育营六四事件ۍ 02.09.2018
comment
Если это проблема, вы можете изменить окончательный mv на cp. Конечно, у вас должны быть права на запись в исходный файл. - person Gene; 02.09.2018
comment
это не работает. команда find показывает, что она находит все файлы, но команда expand работает только с пятью из них, и я не могу понять, почему ... это все *.ts файлы ... - person ChaseMoskal; 31.12.2019
comment
@ChaseMoskal Попробуйте сами дать команды expand и mv для файла, который не был преобразован. Возможно, вы сможете увидеть какое-то разрешение или аналогичную проблему. Это хорошо сработало для сотен людей, поэтому в вашем сценарии использования должно быть что-то уникальное. - person Gene; 31.12.2019

Попробуйте инструмент командной строки expand.

expand -i -t 4 input | sponge output

куда

  • -i используется для раскрытия только ведущих вкладок в каждой строке;
  • -t 4 означает, что каждая вкладка будет преобразована в 4 символа пробела (по умолчанию 8).
  • sponge взят из _ 6_ и избегает очистки входного файла.

Наконец, вы можете использовать gexpand в OSX после установки coreutils с Homebrew (brew install coreutils).

person kev    schedule 19.06.2012
comment
Это одна из GNU_Core_Utilities. - person kev; 19.06.2012
comment
А для тех систем, которые не используют GNU Core Utilities, у вас есть приличный шанс expand быть установленным, поскольку он стандартизирован единой спецификацией Unix Open Group. См. Выпуск 6 за 2001 год, хотя были применены некоторые обновления, следовательно, год публикации - 2004: _ 2_ - person ; 25.07.2013
comment
Вы должны передать -i в expand, чтобы заменять только ведущие табуляции в каждой строке. Это помогает избежать замены вкладок, которые могут быть частью кода. - person Quolonel Questions; 08.08.2014
comment
Можно ли поместить это в цикл for? Когда я пытаюсь это сделать, я получаю пустые выходные файлы - person ThorSummoner; 01.02.2015
comment
как насчет рекурсивно для каждого файла в каталоге? - person ahnbizcad; 10.06.2015
comment
Каждый раз, когда я пытаюсь использовать это, он очищает некоторые (обычно все) файлы. : \ - person ThorSummoner; 23.06.2015
comment
@ThorSummoner: если input - это тот же файл, что и output, bash уничтожает содержимое еще до запуска expand. Так работает >. - person Robert Siemer; 16.09.2015
comment
@ThorSummoner Вы должны изучить sponge, который полезен для получения стандартного вывода и перенаправления его обратно в исходный файл. Он работает, сохраняя весь вывод, поступающий на его стандартный ввод, ожидая завершения конвейера и только затем открывая и записывая исходный файл. Это часть пакета moreutils (часто не устанавливается по умолчанию). - person RaveTheTadpole; 08.10.2016
comment
Примечание. Вы создаете новый файл, и у нового файла могут быть другие разрешения, чем у файла, с которым вы начали. У меня были файлы с разрешением 0600, после использования expand у нового файла было разрешение по умолчанию 0664. Использование sponge и создание нового файла имели тот же эффект. Использование sponge и НЕ создание нового файла сохраняет исходные разрешения. Пример: expand --tabs=4 input | sponge input. Обратите внимание на использование |, а не > в примере sponge. - person CloudNinja; 18.12.2016
comment
расширять -t 4 Foo | Губка Фу - это заклинание, в котором я нуждался - person Chris Hamons; 06.01.2017
comment
@ahnbizcad Я добавил ответ ниже - person daka; 31.01.2017
comment
Спасибо, я понятия не имел об этой утилите. - person nikhil; 12.07.2017

Сбор лучших комментариев из ответа Джина, лучшее решение на сегодняшний день - использование sponge из moreutils.

sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;

Объяснение:

  • ./ рекурсивно ищет из текущего каталога
  • -iname - совпадение без учета регистра (как для *.java, так и для *.JAVA лайков)
  • type -f находит только обычные файлы (без каталогов, двоичных файлов или символических ссылок)
  • -exec bash -c выполнить следующие команды в подоболочке для каждого имени файла, {}
  • expand -t 4 расширяет все табуляции до 4 пробелов
  • sponge впитывает стандартный ввод (из expand) и записывает в файл (тот же самый) *.

ПРИМЕЧАНИЕ: * Простое перенаправление файла (> "$0") здесь не сработает, потому что оно слишком быстро перезапишет файл.

Преимущество: сохраняются все исходные разрешения для файлов, промежуточные tmp файлы не используются.

person not2qubit    schedule 20.04.2017
comment
TIL: чудесная команда sponge после 15 лет использования Linux. Спасибо таинственному рыцарю из интернета. - person sscarduzio; 19.11.2017

Используйте экранирование обратной косой чертой sed.

В Linux:

  • Замените все вкладки одним дефисом во всех файлах * .txt:

    sed -i $'s/\t/-/g' *.txt
    
  • Замените все вкладки на 1 пробел во всех файлах * .txt:

    sed -i $'s/\t/ /g' *.txt
    
  • Замените все вкладки на 4 пробела во всех файлах * .txt:

    sed -i $'s/\t/    /g' *.txt
    

На Mac:

  • Замените все вкладки на 4 пробела во всех файлах * .txt:

    sed -i '' $'s/\t/    /g' *.txt
    
person e9t    schedule 29.10.2015
comment
Этот ответ кажется самым простым. - person Yan King Yin; 12.09.2019

Вы можете использовать общедоступную команду pr (справочная страница здесь). Например, чтобы преобразовать табуляцию в четыре пробела, сделайте следующее:

pr -t -e=4 file > file.expanded
  • -t подавляет заголовки
  • -e=num заменяет табуляцию на num пробелов

Чтобы преобразовать все файлы в дереве каталогов рекурсивно, пропуская двоичные файлы:

#!/bin/bash
num=4
shopt -s globstar nullglob
for f in **/*; do
  [[ -f "$f" ]]   || continue # skip if not a regular file
  ! grep -qI "$f" && continue # skip binary files
  pr -t -e=$num "$f" > "$f.expanded.$$" && mv "$f.expanded.$$" "$f"
done

Логика пропуска двоичных файлов взята из этого сообщения.

ПРИМЕЧАНИЕ.

  1. Это может быть опасно в репозитории git или svn.
  2. Это неправильное решение, если у вас есть файлы кода, которые содержат вкладки, встроенные в строковые литералы.
person codeforester    schedule 04.05.2017
comment
Есть ли преимущество перед expand, учитывая, что оба являются POSIX? Например. есть ли у него возможность встроенного изменения? Безопасность Git по адресу: stackoverflow.com/a/52136507/895245 - person Ciro Santilli 新疆再教育营六四事件ۍ 02.09.2018

Как преобразовать табуляции в пробелы в каждом файле каталога (возможно, рекурсивно)?

Обычно это не то, что вам нужно.

Вы хотите сделать это для изображений png? PDF-файлы? Каталог .git? Ваш Makefile (для которого требуются вкладки)? Дамп SQL на 5 ГБ?

Теоретически вы можете передать множество параметров исключения find или чему-то еще, что вы используете; но он хрупкий и сломается, как только вы добавите другие двоичные файлы.

Вы хотите как минимум:

  1. Пропускать файлы более определенного размера.
  2. Определите, является ли файл двоичным, проверив наличие байта NULL.
  3. Заменяйте табуляции только в начале файла (expand делает это, sed - нет).

Насколько мне известно, не существует "стандартной" утилиты Unix, которая могла бы это сделать, и это не очень просто сделать с помощью однострочника оболочки, поэтому необходим сценарий.

Некоторое время назад я создал небольшой скрипт под названием sanitize_files, который делает именно это. Он также исправляет некоторые другие общие вещи, такие как замена \r\n на \n, добавление завершающего \n и т. Д.

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

Я также хотел бы указать в ответ на некоторые другие ответы здесь, что использование подстановки оболочки не надежный способ сделать это, потому что рано или поздно у вас будет больше файлов чем уместится в ARG_MAX (в современных системах Linux это 128 КБ, что может показаться много, но рано или поздно этого недостаточно).


#!/usr/bin/env python
#
# http://code.arp242.net/sanitize_files
#

import os, re, sys


def is_binary(data):
    return data.find(b'\000') >= 0


def should_ignore(path):
    keep = [
        # VCS systems
        '.git/', '.hg/' '.svn/' 'CVS/',

        # These files have significant whitespace/tabs, and cannot be edited
        # safely
        # TODO: there are probably more of these files..
        'Makefile', 'BSDmakefile', 'GNUmakefile', 'Gemfile.lock'
    ]

    for k in keep:
        if '/%s' % k in path:
            return True
    return False


def run(files):
    indent_find = b'\t'
    indent_replace = b'    ' * indent_width

    for f in files:
        if should_ignore(f):
            print('Ignoring %s' % f)
            continue

        try:
            size = os.stat(f).st_size
        # Unresolvable symlink, just ignore those
        except FileNotFoundError as exc:
            print('%s is unresolvable, skipping (%s)' % (f, exc))
            continue

        if size == 0: continue
        if size > 1024 ** 2:
            print("Skipping `%s' because it's over 1MiB" % f)
            continue

        try:
            data = open(f, 'rb').read()
        except (OSError, PermissionError) as exc:
            print("Error: Unable to read `%s': %s" % (f, exc))
            continue

        if is_binary(data):
            print("Skipping `%s' because it looks binary" % f)
            continue

        data = data.split(b'\n')

        fixed_indent = False
        for i, line in enumerate(data):
            # Fix indentation
            repl_count = 0
            while line.startswith(indent_find):
                fixed_indent = True
                repl_count += 1
                line = line.replace(indent_find, b'', 1)

            if repl_count > 0:
                line = indent_replace * repl_count + line

        data = list(filter(lambda x: x is not None, data))

        try:
            open(f, 'wb').write(b'\n'.join(data))
        except (OSError, PermissionError) as exc:
            print("Error: Unable to write to `%s': %s" % (f, exc))


if __name__ == '__main__':
    allfiles = []
    for root, dirs, files in os.walk(os.getcwd()):
        for f in files:
            p = '%s/%s' % (root, f)
            if do_add:
                allfiles.append(p)

    run(allfiles)
person Martin Tournoij    schedule 12.08.2015
comment
В git выполнить двоичную проверку очень просто: stackoverflow.com/a/52136507/895245 - person Ciro Santilli 新疆再教育营六四事件ۍ 02.09.2018

Мне нравится приведенный выше пример «найти» для рекурсивного приложения. Чтобы адаптировать его к нерекурсивному, изменяя только файлы в текущем каталоге, соответствующие подстановочному знаку, расширения оболочки оболочки может быть достаточно для небольшого количества файлов:

ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v

Если вы хотите, чтобы он молчал после того, как вы уверены, что он работает, просто вставьте -v в команду sh в конце.

Конечно, вы можете выбрать любой набор файлов в первой команде. Например, вы можете указать только определенный подкаталог (или каталоги) контролируемым образом следующим образом:

ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh

Или, в свою очередь, запустите find (1) с некоторой комбинацией параметров глубины и т. Д .:

find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh
person drchuck    schedule 29.04.2014
comment
Подстановка оболочки рано или поздно сломается, потому что общее количество имен файлов может быть только ARG_MAX длины. В системах Linux это 128 КБ, но я сталкивался с этим пределом достаточно раз, чтобы не полагаться на подстановку оболочки. - person Martin Tournoij; 12.08.2015
comment
Вам действительно не нужно их адаптировать. find можно указать -maxdepth 1, и он обрабатывает только записи изменяемого каталога, а не все дерево. - person ShadowRanger; 30.10.2015

Я рекомендую использовать:

find . -name '*.lua' -exec ex '+%s/\t/  /g' -cwq {} \;

Комментарии:

  1. Используйте редактирование на месте. Храните резервные копии в VCS. Нет необходимости создавать файлы * .orig. Хорошая практика - сравнить результат с вашим последним коммитом, чтобы в любом случае убедиться, что это сработало так, как ожидалось.
  2. sed - это редактор потока. Используйте ex для редактирования на месте. Это позволяет избежать создания дополнительных временных файлов и создания оболочек для каждой замены, как в главный ответ.
  3. ВНИМАНИЕ: это портит все вкладки, а не только те, которые используются для отступов. Также он не выполняет контекстно-зависимую замену вкладок. Этого было достаточно для моего случая использования. Но может быть для вас неприемлемым.
  4. РЕДАКТИРОВАТЬ: в более ранней версии этого ответа использовалось find|xargs вместо find -exec. Как указывает @ gniourf-gniourf, это приводит к проблемам с пробелами, кавычками и управляющими символами в именах файлов cf. Wheeler.
person Heinrich Hartmann    schedule 14.06.2016
comment
ex может быть недоступен в каждой системе Unix. Замена на vi -e может сработать на других машинах. Кроме того, ваше регулярное выражение заменяет любое количество начальных символов табуляции двумя пробелами. Замените регулярное выражение на +%s/\t/ /g, чтобы не разрушать многоуровневые отступы. Однако это также влияет на символы табуляции, которые не используются для отступов. - person Lukas Schmelzeisen; 14.06.2016
comment
ex является частью POSIX [1], поэтому должен быть доступен. Хорошее замечание о многоуровневой индендации. На самом деле я использовал вариант /\t/ / для своих файлов, но выбрал /\t\+//, чтобы не нарушать вкладки без отступов. Пропустили проблемы с несколькими отступами! Обновление ответа. [1] man7.org/linux/man -pages / man1 / ex.1p.html # SEE% C2% A0ALSO - person Heinrich Hartmann; 14.06.2016
comment
Использование xargs таким образом бесполезно, неэффективно и некорректно (подумайте о именах файлов, содержащих пробелы или кавычки). Почему бы вам вместо этого не использовать переключатель find -exec? - person gniourf_gniourf; 14.06.2016
comment
Я бы сказал, что имена файлов с пробелами и кавычками не работают; ) Если вам нужно это поддержать, я бы выбрал: -print0 параметры для поиска / xargs. Мне нравится xargs вместо -exec, поскольку: а) разделение задач б) его легче заменить на параллельный GNU. - person Heinrich Hartmann; 14.06.2016
comment
Обновлено добавление комментариев @gniourf_gniourf. - person Heinrich Hartmann; 14.06.2016
comment
Кто сказал, что имена файлов с пробелами разбиты? мы не в Windows, мы в Linux. Имя файла может быть любой действительной непустой строкой C, которая не содержит / (поскольку я упоминал строку C, нулевой байт, конечно, запрещен). Надежный, портативный и эффективный способ - использовать -exec: find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;. xargs следует использовать только в очень специфических ситуациях (которые мне еще предстоит выяснить). - person gniourf_gniourf; 14.06.2016
comment
Уиллер довольно красноречиво сформулировал проблему имени файла UNIX: dwheeler.com/essays /fixing-unix-linux-filenames.html Разрешение произвольных C-строк, вероятно, изначально было не лучшим дизайнерским решением. Когда я контролирую имена, я стараюсь избегать пробелов и управляющих символов. Но опять же, вы явно правы. К ответу добавил комментарий: -print0/-0 тоже правильно, и ИМХО чище. - person Heinrich Hartmann; 14.06.2016
comment
Подумал об этом еще раз. Принят вариант -exec. Спасибо за комментарии! - person Heinrich Hartmann; 15.06.2016

Для этого вы можете использовать find с пакетом tabs-to-spaces.

Сначала установите tabs-to-spaces

npm install -g tabs-to-spaces

затем запустите эту команду из корневого каталога вашего проекта;

find . -name '*' -exec t2s --spaces 2 {} \;

Это заменит каждый символ tab на 2 spaces в каждом файле.

person Harsh Vakharia    schedule 12.01.2017

Я использовал astyle, чтобы изменить отступ всего кода C / C ++ после обнаружения смешанных табуляции и пробелов. У него также есть опции для принудительного использования определенного стиля скобок, если хотите.

person Theo Belaire    schedule 29.09.2013

Для этого можно использовать vim:

find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;

Как заявил Carpetsmoker, он будет изменять настройки в соответствии с вашими vim настройками. И моделины в файлах, если есть. Также он заменит табуляции не только в начале строк. Обычно это не то, что вам нужно. Например, у вас могут быть литералы, содержащие вкладки.

person x-yuri    schedule 10.11.2014
comment
:retab изменит все вкладки в файле, а не те, что были в начале. это также зависит от ваших :tabstop и :expandtab настроек в vimrc или modeline, поэтому это может вообще не работать. - person Martin Tournoij; 12.08.2015
comment
@Carpetsmoker Хорошее замечание о вкладках в начале строк. Может ли какое-либо из представленных здесь решений справиться с этим случаем? Что касается настроек tabstop и expandtab, это сработает, если вы используете vim. Если в файлах нет строк режима. - person x-yuri; 12.08.2015
comment
@ x-yuri хороший вопрос, но в целом спорный. Большинство людей используют \ t не настоящие вкладки в литералах. - person Ricardo Magalhães Cruz; 04.12.2015

Чтобы рекурсивно преобразовать все файлы Java в каталоге, чтобы использовать 4 пробела вместо табуляции:

find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
person Raffi Khatchadourian    schedule 29.06.2016
comment
Чем этот ответ отличается от этого, опубликованного 4 года назад? - person P.P; 29.06.2016
comment
Ваш ответ тоже. Фактически, это неполноценная версия ответа Джина: 1) Ответ Джина позаботится о каталогах с тем же именем. 2) Он не перемещается, если раскрыть не удалось. - person P.P; 30.06.2016

Загрузите и запустите следующий сценарий, чтобы рекурсивно преобразовать жесткие вкладки в программные вкладки в простых текстовых файлах.

Выполните сценарий из папки, содержащей файлы с обычным текстом.

#!/bin/bash

find . -type f -and -not -path './.git/*' -exec grep -Iq . {} \; -and -print | while read -r file; do {
    echo "Converting... "$file"";
    data=$(expand --initial -t 4 "$file");
    rm "$file";
    echo "$data" > "$file";
}; done;
person daka    schedule 31.01.2017

Удобный для репозитория метод Git

git-tab-to-space() (
  d="$(mktemp -d)"
  git grep --cached -Il '' | grep -E "${1:-.}" | \
    xargs -I'{}' bash -c '\
    f="${1}/f" \
    && expand -t 4 "$0" > "$f" && \
    chmod --reference="$0" "$f" && \
    mv "$f" "$0"' \
    '{}' "$d" \
  ;
  rmdir "$d"
)

Действовать для всех файлов в текущем каталоге:

git-tab-to-space

Действовать только с файлами C или C ++:

git-tab-to-space '\.(c|h)(|pp)$'

Вы, вероятно, захотите этого, особенно из-за этих надоедливых файлов Makefile, которые требуют вкладок.

Команда git grep --cached -Il '':

  • перечисляет только отслеживаемые файлы, поэтому внутри .git ничего нет
  • исключает каталоги, двоичные файлы (будут повреждены) и символические ссылки (будут преобразованы в обычные файлы)

как объяснено на странице: Как вывести список всех текстовых (небинарных) файлов в репозитории git?

chmod --reference сохраняет права доступа к файлам без изменений: https://unix.stackexchange.com/questions/20645/clone-ownership-and-permissions-from-another-file К сожалению, я не могу найти краткую альтернативу POSIX.

Если в вашей кодовой базе возникла безумная идея разрешить функциональные необработанные вкладки в строках, используйте:

expand -i

а затем получайте удовольствие, просматривая все вкладки, не начинающиеся с начала строки, одну за другой, которые вы можете перечислить с помощью: Можно ли использовать git grep для вкладок?

Проверено на Ubuntu 18.04.

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 02.09.2018

Никто не упоминается rpl? Используя rpl, вы можете заменить любую строку. Чтобы преобразовать табуляцию в пробелы,

rpl -R -e "\t" "    "  .

очень простой.

person PeopleMoutainPeopleSea    schedule 22.03.2019
comment
Это повредило все двоичные файлы в моем репо. - person Aaron Franke; 06.11.2019
comment
Отличная команда, но потенциально опасная с рекурсивным параметром и всеми файлами в папке, как указано выше. Я бы добавил опцию --dry-run на всякий случай, чтобы убедиться, что вы находитесь в нужной папке. - person MortimerCat; 06.01.2020

Использование expand, как предлагается в других ответах, кажется наиболее логичным подходом только для этой задачи.

Тем не менее, это также можно сделать с помощью Bash и Awk на случай, если вы захотите внести другие изменения вместе с ним.

При использовании Bash 4.0 или более поздней версии встроенная функция shopt globstar можно использовать для рекурсивного поиска с **.

В GNU Awk версии 4.1 или более поздней версии sed могут быть изменены "на месте":

shopt -s globstar
gawk -i inplace '{gsub("\t","    ")}1' **/*.ext

Если вы хотите установить количество пробелов на вкладке:

gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext
person John B    schedule 31.03.2015

Преобразование вкладок в пространство всего в файлах ".lua" [вкладки -> 2 пробела]

find . -iname "*.lua" -exec sed -i "s#\t#  #g" '{}' \;
person Makah    schedule 26.10.2013
comment
Очевидно, что количество места, на которое расширяется вкладка, зависит от контекста. Таким образом, sed - совершенно неподходящий инструмент для этой задачи. - person Sven; 30.03.2015
comment
?? @Sven, моя команда sed делает то же самое, что и команда расширения (expand -t 4 input >output) - person Makah; 31.03.2015
comment
Конечно, нет. expand -t 4 расширит вкладку в a\tb до 3 пробелов, а табуляцию в aa\tb до 2 пробелов, как и должно быть. expand учитывает контекст вкладки, sed не учитывает и заменяет вкладку указанным вами количеством пробелов, независимо от контекста. - person Sven; 31.03.2015

Используйте vim-way:

$ ex +'bufdo retab' -cxa **/*.*
  • Сделайте резервную копию! перед выполнением указанной выше команды, так как это может повредить ваши двоичные файлы.
  • Чтобы использовать globstar (**) для рекурсии, активируйте shopt -s globstar.
  • Чтобы указать конкретный тип файла, используйте, например: **/*.c.

Чтобы изменить позицию табуляции, добавьте +'set ts=2'.

Однако недостатком является то, что он может заменять табуляции внутри строк.

Итак, для немного лучшего решения (с помощью подстановки) попробуйте:

$ ex -s +'bufdo %s/^\t\+/  /ge' -cxa **/*.*

Или с помощью ex editor + expand утилиты:

$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*

Информацию о конечных пробелах см .: Как удалить конечные пробелы для нескольких файлов?


Вы можете добавить в свой .bash_profile следующую функцию:

# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
  ex +'set ts=2' +'bufdo retab' -cxa $*
}
person kenorb    schedule 19.04.2015
comment
Я проголосовал против многих ответов в этой теме, не только за ваш ;-) Причины: _ 1_ может вообще не работать, подстановка оболочки - плохое решение для такого рода вещей, ваша :s команда заменит any < / i> количество вкладок с двумя пробелами (чего вы почти никогда не хотите), запускать ex только для запуска :!expand процесса глупо ... - person Martin Tournoij; 12.08.2015
comment
... и все ваши решения будут затирать двоичные файлы и тому подобное (например, файлы .png, .pdf и т. д.) - person Martin Tournoij; 12.08.2015
comment
Откровенно говоря, это ужасное предложение для документации - нужно хорошо знать ряд довольно непрозрачных синтаксических и семантических проблем нескольких программ, чтобы понять это. - person Josip Rodin; 22.05.2016