Преобразование вкладок в пробелы во многих файлах

11

У меня есть много файлов с закрытыми вкладками, и я хотел бы преобразовать их в пробелы. Я знаю о expandкоманде, но, к сожалению, мне пришлось бы печатать каждый файл, используя ее. Есть ли более простой способ сделать это в Linux?

человек
источник

Ответы:

12

Попробуйте следующее:

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

Если вы хотите четыре пробела, попробуйте:

find ./ -type f -exec sed -i 's/\t/    /g' {} \;
Николас Рауль
источник
Это заменит каждую вкладку одним пробелом. Поскольку человек упомянул использование expand, я предполагаю, что он / она хочет сохранить выравнивание текста.
garyjohn
Вам нужно 's/\t/ /g'заменить более одной вкладки на строку.
Даниэль Андерссон
1
Существенное ускорение, если есть много файлов, делает " find ./ -type f -exec sed -i ’s/\t/ /g’ {} +" (то есть " +" вместо " \;"), если findверсия поддерживает это (и я лично не встречал ни одной версии, которая не делает, но это не стандарт POSIX , поэтому я думаю, что это может произойти в некоторых системах. Смотрите " -exec command {} +" в руководстве). Вместо того, чтобы запускать один экземпляр sedдля каждого файла, это создаст список аргументов с таким количеством аргументов имени файла, которое поддерживает система ( getconf ARG_MAX= 2097152 в моей системе), и xargs, таким образом, запустит гораздо меньше sedпроцессов.
Даниэль Андерссон
6
Примечание для всех пользователей Mac, которые находят это: версия OS X sedне понимает \tescape-последовательность вкладки. Вы можете заменить его буквальным символом табуляции, который вы можете ввести в оболочке [Ctrl]+V, [Tab].
Джереми Бэнкс
expandвероятно, лучше, чем sedдля этого, как объяснено в: stackoverflow.com/a/11094620/131824
Дэвид Вайнрауб
6

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

while read file
do
   expand "$file" > /tmp/expandtmp
   mv /tmp/expandtmp "$file"
done

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

garyjohn
источник
3
Сделайте mvусловие успеха expand:expand ... && mv ...
Приостановлено до дальнейшего уведомления.
Не забудьте expand -t 4расширить вкладки до 4 пробелов. Кроме того, этот метод может создавать завершающие строки. Но в остальном это работает.
mgold
3
find . -type f -iname "*.js" -print0 | xargs -0 -I foo tab2space foo foo

-I foo создает переменную шаблона foo для каждой строки ввода, поэтому вы можете ссылаться на нее несколько раз.

-print0и -0скажите обеим командам использовать \ 0 в качестве разделителя строк вместо SPACE, чтобы эта команда работала для путей с пробелами.

Дастин Гетц
источник
1
find -name \*.js -exec bash -c 'expand -t 4 "$0" | tee "$0"' {} \;

Минусы:
файлы, размер которых превышает размер буфера канала ( 64 КБ ), усекаются

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

Raylu
источник
0

Это лучше:

find . -name *.java ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
oDarek
источник
3
Почему это лучше? Это не очень хорошая идея, /tmp/eпотому что, если что-то еще использует этот файл, это испортит его. Например, если два пользователя хотели использовать это одновременно.
Кевин Панко
0

Я решил эту проблему с учетом следующих требований:

  • Фильтруйте файлы по их именам, чтобы обрабатывать, например, только файл .cpp или .json
  • Поддержка параллельной обработки. Если файлов много, это может значительно ускорить
  • Решение должно укладываться в одну строку для удобства использования

Последнее требование было самым трудным для выполнения, потому что «расширение» не позволяет изменять файлы на месте.

Я придумал следующее решение:

find . -type f -regextype egrep -regex '.*\.(c|cpp|h|hpp)'  -print0 | xargs -0 -n 1 -P 10 -IFILE bash -c ' ( echo "Processing FILE..." && expand -t 4 "FILE" > /tmp/expand.$$ && mv /tmp/expand.$$ "FILE" ) || exit 255'

Вот некоторые объяснения:

  • «find» находит файлы для обработки. «-regextype egrep» позволяет фильтровать их по имени и регулярному выражению в формате «egrep».
  • параметр "-type f" гарантирует, что мы будем сопоставлять только обычные файлы, а не, например, каталоги или что-то еще особенное
  • параметр "-regexp" - это само регулярное выражение, которое в данном случае соответствует любому файлу, который заканчивается на .c, .cpp, .h или .hpp (полное имя должно совпадать, поэтому "file.c2" не будет чего мы и хотим)
  • «-print0» указывает «find» печатать пути к файлам на своем стандартном выводе с символом 0 в конце каждого пути. Вместе с опцией "-0" для "xargs" он позволяет передавать имена, содержащие возвратные каретки, из одного инструмента в другой (даже если это довольно редкая ситуация ...)
  • xargs запускает новый процесс для каждого пути ("-n 1"), но может запускать до 10 процессов параллельно ("-P 10")
  • xargs использует псевдоним «FILE» для передачи каждого пути к файлу в команду, которая является сценарием bash
  • скрипт bash вызывает «развернуть» и сохраняет результат во временном файле, имена которого содержат идентификатор текущего процесса ($$), так что все процессы, запущенные параллельно для данного файла, используют разные временные файлы
  • вся команда использует шаблон (command1 && command2 && command3), поэтому процесс остановится, если какая-либо подкоманда вернет ошибку
  • если в предыдущей цепочке & & есть какая-либо ошибка, сценарий bash вернет код выхода 255, который немедленно остановит xargs
ocroquette
источник