У меня есть много файлов с закрытыми вкладками, и я хотел бы преобразовать их все в пробелы. Я знаю о команде expand
, но, к сожалению, мне пришлось бы печатать каждый файл, используя его. Есть ли более простой способ сделать это в Linux?
6 ответов
Попробуйте следующее:
find ./ -type f -exec sed -i 's/\t/ /g' {} \;
Если вы хотите четыре пробела, попробуйте:
find ./ -type f -exec sed -i 's/\t/ /g' {} \;
Есть много способов сделать это. Есть также много способов выстрелить себе в ногу, делая это, если вы неосторожны или если вы новичок в Linux, как кажется. Предполагая, что вы можете создать список файлов, которые вы хотите преобразовать, используя что-то вроде find
или вручную с редактором, просто передайте этот список в следующий список.
while read file
do
expand "$file" > /tmp/expandtmp
mv /tmp/expandtmp "$file"
done
Один из способов, которым вы можете выстрелить себе в ногу, - это сделать опечатку так, чтобы вы добавили пустой файл ко всем указанным вами именам файлов, тем самым удалив содержимое всех ваших файлов. Поэтому будьте осторожны и тестируйте все, что вы делаете в первую очередь, на небольшом наборе файлов, для которого вы создали резервную копию.
find . -type f -iname "*.js" -print0 | xargs -0 -I foo tab2space foo foo
-I foo
создает переменную шаблона foo для каждой строки ввода, так что вы можете ссылаться на вход более одного раза.
-print0
и -0
указывают обеим командам использовать \0 в качестве разделителя строк вместо SPACE, поэтому эта команда работает для путей с пробелами.
find -name \*.js -exec bash -c 'expand -t 4 "$0" | tee "$0"' {} \;
Минусы:
файлы, размер которых превышает размер буфера канала (64 КБ), усекаются
Плюсы:
нет временных файлов
файлы, размер которых превышает размер буфера канала, усекаются
Я решил эту проблему с учетом следующих требований:
- Фильтруйте файлы по их именам, чтобы обрабатывать, например, только файл .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
Это лучше:
find . -name *.java ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;