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

175

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

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

  • 0
    Вы хотите заменить вкладки в файлах или именах файлов?
  • 0
    не в именах файлов, а в файлах.
Показать ещё 1 комментарий
Теги:
in-place
spaces

19 ответов

54
Лучший ответ

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

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

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

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

Downsides:

  • Будет заменять вкладки всюду в файле.
  • Пройдет много времени, если в этом каталоге есть дамп SQL объемом 5 ГБ.
  • 9
    для визуального пространства, представляющего собой смесь вкладок и пробелов, этот подход дает неверное расширение.
  • 6
    Я также добавил бы средство сопоставления файлов, например, для поиска только файлов .php ./ -iname "* .php" -type f -exec sed -i 's / \ t / / g' {} \;
Показать ещё 9 комментариев
264

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

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

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

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

  • 2
    Я должен скопировать эту вещь куда-нибудь, потому что я использую это очень часто.
  • 0
    Может ли кто-нибудь объяснить, почему использовать _ в команде, а не опускать ее и использовать $ 0?
Показать ещё 33 комментария
136

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

expand -i -t 4 input | sponge output

где

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

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

  • 5
    Это одна из GNU_Core_Utilities
  • 2
    А для тех систем, которые не используют GNU Core Utilities, у вас есть приличный шанс установить expand , поскольку оно стандартизировано Спецификацией Open Unix для Open Group. См. Выпуск 6, выпущенный в 2001 году, хотя некоторые обновления были применены, поэтому год публикации был 2004: expand
Показать ещё 10 комментариев
9

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

В linux:

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

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

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

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

На mac:

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

    sed -i '' $'s/\t/    /g' *.txt
    
  • 0
    это работает рекурсивно?
  • 1
    @ Маша sed -i '' $'s/\t/ /g' $(find . -name "*.txt")
6

Собирая лучшие комментарии от Ответ на гены, лучшим решением на сегодняшний день является использование 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 расширяет все TAB до 4 пробелов
  • sponge впитайте стандартный ввод (из expand) и напишите в файл (тот же) *.

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

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

  • 1
    TIL: чудесная команда sponge после 15 лет использования Linux. Спасибо загадочному рыцарю из интернета.
3

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

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
  • 1
    Глобализация оболочки рано или поздно прекратится, потому что общее количество имен файлов может иметь длину только ARG_MAX . Это 128 КБ в системах Linux, но я сталкивался с этим ограничением достаточно раз, чтобы не полагаться на глобализацию оболочки.
  • 1
    Вам не нужно адаптировать их. find можно сказать -maxdepth 1 , и он обрабатывает только записи изменяемого каталога, а не все дерево.
2

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

Обычно это не, что вы хотите.

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

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

Что вы хотите, по крайней мере:

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

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

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

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

Я также хотел бы отметить, что в ответ на некоторые другие ответы здесь, что использование globbing оболочки не - это надежный способ сделать это, потому что раньше или позже вы получите больше файлов, чем будет в 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 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)
  • 0
    В git проверка двоичного кода проста: stackoverflow.com/a/52136507/895245
1

Вы можете использовать общедоступную команду 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

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

Примечание:

  • Это может быть опасно в git или svn repo
  • Это неправильное решение, если у вас есть файлы кода с вкладками, встроенными в строковые литералы
  • 0
    Любое преимущество перед expand учитывая, что оба POSIX? Например, у него есть опция встроенного изменения? Git Safety по адресу: stackoverflow.com/a/52136507/895245
1

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

Выполните script из папки, содержащей текстовые файлы.

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

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

find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;
  • 0
    Чем этот ответ отличается от того, который был опубликован 4 года назад?
  • 0
    @PP Использует команду expand .
Показать ещё 1 комментарий
1

Моя рекомендация:

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

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

  • Используйте редактирование на месте. Храните резервные копии в VCS. Не нужно создавать файлы *.orig. Хорошая практика заключается в том, чтобы отличать результат от вашей последней фиксации, чтобы убедиться, что это сработало, как ожидалось, в любом случае.
  • sed - редактор потока. Используйте ex для редактирования на месте. Это позволяет избежать создания дополнительных временных файлов и нерестовых оболочек для каждой замены, как в верхнем ответе.
  • ПРЕДУПРЕЖДЕНИЕ. Это противоречит всем вкладкам, а не только тем, которые используются для отступов. Кроме того, он не выполняет контекстно-зависимую замену вкладок. Этого было достаточно для моего варианта использования. Но может быть неприемлемо для вас.
  • EDIT: более ранняя версия этого ответа использовала find|xargs вместо find -exec. Как указано в @gniourf-gniourf, это приводит к проблемам с пробелами, кавычками и контрольными символами в именах файлов ср. Wheeler.
  • 0
    ex может быть недоступен в каждой системе Unix. Замена его на vi -e может работать на большем количестве машин. Кроме того, ваше регулярное выражение заменяет любое количество начальных символов табуляции двумя пробелами. Замените регулярное выражение на +%s/\t/ /g чтобы не разрушать многоуровневые отступы. Однако это также влияет на символы табуляции, которые не используются для отступа.
  • 0
    ex является частью POSIX [1], поэтому должен быть доступен. Хорошее замечание о многоуровневой индендации. Я действительно использовал вариант /\t/ / в моих файлах, но выбрал /\t\+// чтобы не ломать вкладки без отступов. Пропустил проблемы с несколькими отступами! Обновление ответа. [1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
Показать ещё 6 комментариев
1

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

0

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

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

npm install -g tabs-to-spaces

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

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

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

0

Если вы хотите заменить вкладки на пробелы, например, файлы *.c и *.h, следующая команда - лучшая: find . -name "*.c" -o -name "*.h" | xargs -IFILE -t bash -c " expand -t 4 FILE > tmp ; cat tmp > FILE"

Тогда rm -f tmp

0

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

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

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

  • 0
    :retab изменит все вкладки в файле, а не те, что в начале. это также зависит от того, :expandtab настройки :tabstop и :expandtab находятся в vimrc или modeline, так что это может вообще не работать.
  • 0
    @Carpetsmoker Хорошее замечание о вкладках в начале строк. Любое из решений здесь обрабатывает этот случай? Что касается tabstop и expandtab , то это сработает, если вы используете vim . Если у вас нет строк режима в файлах.
Показать ещё 1 комментарий
-1

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

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

Чтобы изменить tabstop, добавьте +'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 $*
}
  • 0
    Я отклонил многие ответы в этой теме, не только ваши ;-) Причины :retab может не работать вообще , :retab оболочки - плохое решение для такого рода вещей , ваша команда :s заменит любое количество вкладок двумя пробелами (что вам почти никогда не нужно), запускать ex просто для запуска :!expand
  • 0
    ... и все ваши решения будут блокировать двоичные файлы и тому подобное (например, файлы .png, файлы .pdf и т. д.)
Показать ещё 1 комментарий
-1

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

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

При использовании Bash 4.0 или выше shopt builtin globstar может использоваться для рекурсивного поиска с помощью **.

С GNU Awk версии 4.1 или выше, sed, например, "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
-2

Если вы не против использования notepad ++ или подобного программного обеспечения, откройте все упомянутые файлы в блокноте ++, затем перейдите на вкладку замены (shortcut ctrl + h)

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

заменить вкладку пробелом в блокноте ++

Существует также альтернатива, которая может лучше соответствовать вашим потребностям, вы можете сделать это на вкладке "Найти файлы", выбрать каталог и выбрать определенные файлы с помощью фильтра типов, а остальные - как раньше.

-2

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

find . -iname "*.lua" -exec sed -i "s#\t#  #g" '{}' \;
  • 0
    Очевидно, что объем пространства, на которое расширяется вкладка, зависит от контекста. Таким образом, sed - совершенно неподходящий инструмент для этой задачи.
  • 0
    ?? @Sven, моя команда sed делает то же самое, что и команда expand -t 4 input >output ( expand -t 4 input >output )
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню