Як я можу конвертувати вкладки у пробіли у кожному файлі каталогу (можливо, рекурсивно)?
Також чи існує спосіб встановлення кількості пробілів на вкладці?
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 '*'
, я щойно знищив своє місцеве git repo
Спробуйте інструмент командного рядка 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
На mac:
Замініть всі вкладки 4 місцями у всіх * .txt файлах:
sed -i '' $'s/\t/ /g' *.txt
sed -i '' $'s/\t/ /g' $(find . -name "*.txt")
Ви можете використовувати загальнодоступну pr
команду ( тут розміщена сторінка man ). Наприклад, щоб перетворити вкладки на чотири пробіли, зробіть це:
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? Наприклад, чи є варіант зміни вбудованої? Гіт безпеку на: stackoverflow.com/a/52136507/895245
Як я можу конвертувати вкладки у пробіли у кожному файлі каталогу (можливо, рекурсивно)?
Зазвичай це не так те, що ви хочете.
Ви хочете зробити це для зображень PNG? PDF-файли? Каталог .git? Ваш
Makefile
(що вимагає вкладки)? Дамп SQL на 5 Гб?
Теоретично ви могли б передати цілий ряд варіантів виключення до того find
чи іншого, що ви використовуєте; але це неміцно, і воно порушиться, як тільки ви додасте інші двійкові файли.
Що ви хочете, це принаймні:
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)
Мені подобається приклад "знайти" вище для рекурсивного застосування. Щоб адаптувати його до нерекурсивного, лише змінюючи файли в поточному каталозі, що відповідають підстановці, розширення глобальної оболонки може бути достатнім для невеликої кількості файлів:
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
Або в свою чергу виконайте пошук (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, це призводить до проблем із пробілами, лапками та символами керування у назвах файлів пор. Уїлер .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
тих пір: а) Розмежування проблем b) її можна легше замінити на паралель 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 або новішої версії, вбудований вбудований файл 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-шлях:
$ 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
команда замінить будь-яку кількість вкладок на 2 пробіли (що ви майже ніколи не хочу), починати колишнього просто запустити :!expand
процес - нерозумно ...