Как я могу преобразовать вкладки в пробелы в каждом файле каталога (возможно, рекурсивно)?
Кроме того, есть ли способ установить количество пробелов на вкладке?
pr
это замечательная утилита для этого. Смотрите этот ответ .
Как я могу преобразовать вкладки в пробелы в каждом файле каталога (возможно, рекурсивно)?
Кроме того, есть ли способ установить количество пробелов на вкладке?
pr
это замечательная утилита для этого. Смотрите этот ответ .
Ответы:
Предупреждение: это сломает ваш репо.
Это приведет к повреждению бинарных файлов , включая те
svn
, которые находятся под.git
,! Прочитайте комментарии перед использованием!
find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +
Исходный файл сохраняется как [filename].orig
.
Замените '* .java' на конец файла того типа, который вы ищете. Таким образом, вы можете предотвратить случайное повреждение двоичных файлов.
Недостатки:
expand
.
find ./ -type f -exec sed -i 's/^\t/####/g' {} \;
, Но я не знал о команде расширения - очень полезно!
Простая замена sed
- это нормально, но не самое лучшее решение. Если между вкладками есть «лишние» пробелы, они все равно останутся после замены, поэтому поля будут неровными. Вкладки, развернутые в середине строк, также не будут работать правильно. В bash
, мы можем сказать вместо
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
применить expand
к каждому файлу Java в текущем дереве каталогов. Удалите / замените -name
аргумент, если вы ориентируетесь на некоторые другие типы файлов. Как упоминается в одном из комментариев, будьте очень осторожны при удалении -name
или использовании слабого подстановочного знака. Вы можете легко забить хранилище и другие скрытые файлы без намерения. Вот почему первоначальный ответ включал это:
Вы должны всегда делать резервную копию дерева, прежде чем пытаться что-то подобное на случай, если что-то пойдет не так.
{}
. Похоже, он не знал о том, $0
когда -c
используется. Затем dimo414 изменился с моего использования temp в каталоге преобразования на /tmp
, что будет намного медленнее, если он /tmp
находится в другой точке монтирования. К сожалению, у меня нет Linux-бокса для тестирования вашего $0
предложения. Но я думаю, что вы правы.
find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
sponge
из joeyh.name/code/moreutils , вы можете написатьfind . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
find . -name '*'
, я только что уничтожил мой локальный репозиторий
Попробуйте инструмент командной строки expand
.
expand -i -t 4 input | sponge output
где
-i
используется для расширения только ведущих вкладок в каждой строке;-t 4
означает, что каждая вкладка будет преобразована в 4 символа пробела (по умолчанию 8).sponge
из moreutils
пакета, и избегает очистки входного файла .Наконец, вы можете использовать gexpand
на OSX, после установки coreutils
с Homebrew ( brew install coreutils
).
-i
на expand
замену только ведущих вкладок в каждой строке. Это помогает избежать замены вкладок, которые могут быть частью кода.
input
- это тот же файл, что output
и bash, который забивает содержимое еще до запуска expand
. Вот как это >
работает.
Собирая лучшие комментарии от ответа Гена , лучшее решение на сегодняшний день, является использование 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
файлы не используются.
Используйте обратную косую черту sed
.
На Linux:
Замените все вкладки с 1 дефисом во всех файлах * .txt:
sed -i $'s/\t/-/g' *.txt
Замените все вкладки на 1 пробел на месте, во всех файлах * .txt:
sed -i $'s/\t/ /g' *.txt
Замените все вкладки с 4 пробелами во всех * .txt файлах:
sed -i $'s/\t/ /g' *.txt
На маке:
Замените все вкладки с 4 пробелами во всех * .txt файлах:
sed -i '' $'s/\t/ /g' *.txt
sed -i '' $'s/\t/ /g' $(find . -name "*.txt")
Вы можете использовать общедоступную 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
Логика пропуска двоичных файлов взята из этого поста .
НОТА:
expand
что оба являются POSIX? Например, у него есть встроенная опция изменения? Git safety at: stackoverflow.com/a/52136507/895245
Как я могу преобразовать вкладки в пробелы в каждом файле каталога (возможно, рекурсивно)?
Обычно это не то, что вы хотите.
Вы хотите сделать это для изображений PNG? PDF файлы? Каталог .git? Ваш
Makefile
(который требует вкладки)? SQL-дамп 5 Гб
Теоретически вы можете передать множество опций исключения find
или что-то еще, что вы используете; но это хрупко, и сломается, как только вы добавите другие двоичные файлы.
То, что вы хотите, это как минимум:
expand
делает это, sed
не делает).Насколько я знаю, не существует "стандартной" утилиты Unix, которая могла бы сделать это, и это не очень легко сделать с однострочником оболочки, поэтому необходим скрипт.
Некоторое время назад я создал небольшой скрипт под названием
sanitize_files, который делает именно это. Также исправлены некоторые другие распространенные вещи, такие как замена \r\n
на \n
, добавление трейлинга \n
и т. Д.
Вы можете найти упрощенный скрипт без дополнительных функций и аргументов командной строки ниже, но я рекомендую использовать вышеописанный скрипт, так как он с большей вероятностью получит исправления ошибок и другие обновления, чем этот пост.
Я также хотел бы отметить, в ответ на некоторые другие ответы здесь, что использование глобализации оболочки не является надежным способом сделать это, потому что рано или поздно у вас будет больше файлов, чем уместится ARG_MAX
(в современных В системах Linux это 128k, что может показаться большим, но рано или поздно этого не
достаточно).
#!/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)
Мне нравится пример "найти" выше для рекурсивного приложения. Чтобы адаптировать его к нерекурсивному, изменяя только файлы в текущем каталоге, которые совпадают с подстановочными знаками, расширения оболочки может быть достаточно для небольшого количества файлов:
ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v
Если вы хотите, чтобы он молчал после того, как вы уверены, что он работает, просто бросьте команду -v
on 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
ARG_MAX
длины. Это 128 КБ в системах Linux, но я сталкивался с этим ограничением достаточно раз, чтобы не полагаться на глобализацию оболочки.
find
можно сказать -maxdepth 1
, и он обрабатывает только записи изменяемого каталога, а не все дерево.
Для этого можно использовать vim
:
find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;
Как заявил Carpetsmoker, он будет перезагружен в соответствии с вашими vim
настройками. И модели в файлах, если есть. Также он заменит вкладки не только в начале строк. Что не то, что вы обычно хотите. Например, у вас могут быть литералы, содержащие вкладки.
:retab
изменит все вкладки в файле, а не те, что в начале. это также зависит от ваших настроек :tabstop
и :expandtab
настроек в vimrc или modeline, так что это может вообще не работать.
tabstop
и expandtab
настроек, он будет работать, если вы используете vim
. Если у вас нет строк режима в файлах.
Я рекомендую использовать:
find . -name '*.lua' -exec ex '+%s/\t/ /g' -cwq {} \;
Комментарии:
sed
потоковый редактор Используйте ex
для редактирования на месте. Это позволяет избежать создания дополнительных временных файлов и порождать оболочки для каждой замены, как показано в верхнем ответе .find|xargs
вместо find -exec
. Как указывает @ gniourf-gniourf, это приводит к проблемам с пробелами, кавычками и контрольными символами в именах файлов cf. Уилер .ex
может быть недоступно в каждой системе Unix. Замена его vi -e
может работать на большем количестве машин. Кроме того, ваше регулярное выражение заменяет любое количество начальных символов табуляции двумя пробелами. Замените регулярное выражение, +%s/\t/ /g
чтобы не разрушать многоуровневый отступ. Однако это также влияет на символы табуляции, которые не используются для отступа.
/\t/ /
вариант в моих файлах, но решил /\t\+//
не ломать вкладки без отступов. Пропустил проблемы с несколькими отступами! Обновление ответа. [1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
xargs
таким способом бесполезно, неэффективно и неэффективно (представьте имена файлов, содержащие пробелы или кавычки). Почему бы вам не использовать find
«s -exec
переключатель вместо этого?
-print0
опции для поиска / xargs. Мне больше нравится xargs, -exec
так как: а) разделение проблем; б) его можно поменять местами с помощью параллельной GNU.
Чтобы рекурсивно преобразовать все файлы Java в каталоге, чтобы использовать 4 пробела вместо вкладки:
find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
Вы можете использовать find
с tabs-to-spaces
пакетом для этого.
Сначала установите tabs-to-spaces
npm install -g tabs-to-spaces
затем запустите эту команду из корневого каталога вашего проекта;
find . -name '*' -exec t2s --spaces 2 {} \;
Это заменит каждый tab
символ с 2 spaces
в каждом файле.
Тело не упомянуто rpl
? Используя rpl вы можете заменить любую строку. Чтобы преобразовать вкладки в пробелы,
rpl -R -e "\t" " " .
очень просто.
Использование, expand
как предлагается в других ответах, кажется наиболее логичным подходом для одной только этой задачи.
Тем не менее, это также может быть сделано с Bash и Awk на случай, если вы захотите сделать некоторые другие модификации вместе с ним.
Если вы используете Bash 4.0 или выше, встроенный модуль shopt globstar
может использоваться для рекурсивного поиска **
.
С помощью GNU Awk версии 4.1 или выше можно внести изменения, подобные файлам «inplace»:
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
Загрузите и запустите следующий сценарий для рекурсивного преобразования жестких вкладок в программные вкладки в текстовых файлах.
Выполните сценарий из папки, которая содержит текстовые файлы.
#!/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;
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)$'
Вы, вероятно, хотите этого особенно из-за тех раздражающих Makefiles, которые требуют вкладок.
Команда git grep --cached -Il ''
:
.git
как объяснено на: Как перечислить все текстовые (недвоичные) файлы в репозитории git?
chmod --reference
оставляет права доступа к файлу без изменений: /unix/20645/clone-ownership-and-permissions-from-another-file К сожалению, я не могу найти краткую альтернативу POSIX .
Если ваша кодовая база имела безумную идею разрешить функциональные необработанные вкладки в строках, используйте:
expand -i
и затем получайте удовольствие, просматривая все незапуски линейных вкладок одну за другой, с помощью которых вы можете перечислить: Возможно ли использовать git grep для вкладок?
Проверено на Ubuntu 18.04.
Преобразование вкладок в пробелы только в файлах ".lua" [вкладки -> 2 пробела]
find . -iname "*.lua" -exec sed -i "s#\t# #g" '{}' \;
expand -t 4 input >output
)
expand -t 4
развернет вкладку a\tb
до 3 пробелов и вкладку aa\tb
до 2 пробелов, как и должно быть. expand
учитывает контекст вкладки, sed
не изменяет и заменяет вкладку количеством пробелов, указанным вами, независимо от контекста.
Используйте vim-way:
$ ex +'bufdo retab' -cxa **/*.*
globstar
( **
) для рекурсии, активируйте с помощью shopt -s globstar
.**/*.c
.Чтобы изменить вкладку, добавьте +'set ts=2'
.
Однако недостатком является то, что он может заменить вкладки внутри строк .
Так что для немного лучшего решения (используя замену), попробуйте:
$ ex -s +'bufdo %s/^\t\+/ /ge' -cxa **/*.*
Или используя ex
редактор + 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 $*
}
:retab
может не работать вообще , глобализация оболочки - плохое решение для такого рода вещей , ваша :s
команда заменит любое количество вкладок двумя пробелами (которые вы почти никогда не хочу), начинать бывшего просто чтобы запустить :!expand
процесс глупо ...