gzip все файлы с определенными расширениями

11

Я пытаюсь сжать все файлы в Ubuntu, которые имеют расширение .css, .html или .js. в верхнем каталоге и во всех подкаталогах. Я хочу сохранить исходные файлы и перезаписать файл .gz, если он уже существует.

Поэтому, когда у меня есть n файлов, я хочу сохранить эти n файлов и создать дополнительные n архивных файлов. Не только один.

Я попытался запустить скрипт, который выглядит следующим образом:

gzip -rkf *.css
gzip -rkf *.html
... one line for each file extension

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

Второе и более важное: это не работает. Хотя -r должен выполнить эту работу, подкаталоги не изменились. Файл gzip создается только в верхнем каталоге.

Что мне здесь не хватает?

Кстати: ниже приведена ошибка в подробном выводе, верно? При использовании параметров -k и -v

-k, --keep        keep (don't delete) input files
-v, --verbose     verbose mode

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

$ ls
  index.html      subdir1  testfile      testfile.css.gz
  javaclass.java  subdir2  testfile.css
$ gzip -fkv *.css
  testfile.css:   6.6% -- replaced with testfile.css.gz
$ ls
  index.html      subdir1  testfile      testfile.css.gz
  javaclass.java  subdir2  testfile.css
Садик
источник
1
-rработает как задумано. От man gzip : рекурсивно путешествовать по структуре каталогов. Если какое-либо из имен файлов, указанных в командной строке, является каталогом , gzip спустится в каталог и сожмет все найденные там файлы (или распакует их в случае gunzip). (выделение мое)
Деннис
ОК. Поэтому -r будет вводить каталог с именем XYZ.css. Тогда рекурсия не разработана, как я ожидал.
Садик

Ответы:

7

Вы можете сделать это с помощью цикла for, чтобы найти каждый файл и затем сжать его:

for i in `find | grep -E "\.css$|\.html$"`; do gzip "$i" ; done
MNDO
источник
Спасибо! Хотя -rопция не работает -kи -fработает, поэтому я могу использовать их следующим образом: для я в find | grep -E "\.css$|\.html$"; do gzip -vkf "$ i"; done`
Садик
@ Садик: Будь осторожен! Этот подход не будет работать, если любое из имен файлов содержит пробел.
Деннис
Не могли бы вы объяснить, почему нет?
Садик
1
@ Садик: `...`предоставляет строку, а не список. forиспользует внутренний разделитель полей ( $IFS), чтобы решить, где должна быть разбита эта строка. По умолчанию он разделяется на переводы строк, табуляции и пробелы, поэтому, если у вас есть файл с именем new style.css, команды gzip newи gzip style.cssбудут выполнены.
Деннис
1
@Sadik, Денис прав, как быстрый обходной путь, который вы можете запустить export IFS=$'\n'непосредственно перед forциклом.
MNDO
14

я хотел бы использовать

find /path/to/dir \( -name '*.css' -o -name '*.html' \) -exec gzip --verbose --keep {} \;

Измените nameна, inameесли вы хотите сопоставлять расширения без учета регистра (т.е. включать .CSSи / или .HTMLрасширения). Вы можете опустить, /path/to/dirесли вы хотите начать рекурсивный поиск из текущего каталога.

steeldriver
источник
2
Для тех, кто интересуется --keepпереключателем, да, это приводит к сохранению исходных файлов. Опустите его, если вы хотите, чтобы они были удалены после gzip.
Бен Джонсон
4

Чтобы получить список файлов:

find -type f | grep -P '\.js|\.html|\.css'

И сжать все эти файлы:

find -type f | grep -P '\.js|\.html|\.css' | tar cvzf archive.gz -T -
хаос
источник
Не будет ли это список файлов , как выход на , а не сами файлы? tarfind
Иос
Я отредактировал свой вопрос, чтобы понять, что я хочу иметь архивный файл для каждого файла CSS, HTML или JS.
Садик
2
@Jos no с -Tопцией tarобрабатывает ввод как имена файлов.
хаос
@chaos Ах, спасибо. Я узнал кое-что сегодня.
Джос
2

Я использовал ответ steeldriver в , но я хотел бы завершить его с --bestи --forceопциями.

cdв любую папку и введите этот код. Все ваши соответствующие файлы будут сжаты.

find . \( -name '*.css' -o -name '*.js' \) -exec gzip --verbose --keep --best --force {} \;
  • Используйте --bestдля лучшей степени сжатия.
  • Используйте --forceдля перезаписи, не спрашивая, есть ли уже сжатый файл.
azerafati
источник
1

Вы можете использовать Globstar.

С globstarвключенной опцией оболочки все, что вам нужно, это gzip -vk **/*.{css,html}.

Bash оболочка имеет globstarопцию , которая позволяет писать рекурсивные шарики с **. shopt -s globstarпозволяет это. Но вы, возможно, не захотите делать это для других команд, которые вы запускаете позже, поэтому вместо этого вы можете запустить его и свою gzip команду в подоболочке .

Эта команда хранит gzipвсе .cssи .htmlфайлы в текущем каталоге, любые его подкаталоги, любые их подкаталоги и т. Д., Сохраняя исходные файлы ( -k) и сообщая вам, что она делает ( -v):

(shopt -s globstar; gzip -vk **/*.{css,html})

Если вы хотите сопоставлять имена файлов без учета регистра, чтобы включить эти расширения с несколькими или всеми заглавными буквами, то вы также можете включить параметр nocaseglobоболочки:

(shopt -s globstar nocaseglob; gzip -vk **/*.{css,html})

;разделяет две команды, и внешняя ( )причина их запуска в подоболочке. Установка параметра оболочки в подоболочке не приводит к его установке в вызывающей оболочке. Если вы действительно хотите включить , globstarто вы можете запустить shopt -s globstar; тогда вы можете просто запустить команду:

gzip -vk **/*.{css,html}

Вы можете отключить globstarс помощью shopt -u globstar. Вы можете проверить, включен ли он в данный момент shopt globstar.

Как это устроено

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

  • Расширение скобки превращается **/*.{css,html}в **/*.css **/*.html.
  • Затем глобализация расширяет эти два шаблона в имена файлов, доступных в текущем каталоге ( **из-за globstar), имена файлов которых состоят из чего-либо ( *), за которым следует указанный суффикс ( .cssили .htmlв этом случае).

Это не соответствует файлам, чьи имена начинаются с. тех, которые находятся в каталогах, названных таким образом. Вероятно, у вас нет таких файлов HTML и CSS, и, если у вас есть, вы, вероятно, не хотите их включать. Но если вы хотите включить их, то вы можете явно сопоставить их в зависимости от ваших потребностей. Например, переход **/*.{css,html}на **/{,.}*.{css,html}включаемые файлы, которые начинаются с поиска, .но по-прежнему не выполняется поиск в папках.

Если вы хотите, чтобы включались и файлы, имена которых начинаются с, .и файлы в каталогах, имена которых начинаются с ., есть более простой и понятный способ: включите параметр dotglobоболочки.

(shopt -s globstar dotglob; gzip -vk **/*.{css,html})

Или, если вы хотите, чтобы регистронезависимое совпадение и совпадение имен файлов начиналось с .:

(shopt -s globstar nocaseglob dotglob; gzip -vk **/*.{css,html})

Возможно, хотя и очень редко, **расширяться до чего-то слишком длинного.

Если у вас есть огромное количество файлов, названных таким образом, это может привести к ошибке с сообщением об ошибке, объясняющим, что оболочка не может создать командную строку, потому что она будет слишком длинной. (Даже с тысячами файлов это обычно не проблема.)

gzip не будет вызван вообще, так что вы не получите наполовину выполненную работу.

Если эта ошибка происходит, или если вы беспокоитесь об этом, вы можете использовать findс -exec, либо как steeldriver описывает{} \;) или как я опишу ниже (с {} +).

Вы можете использовать findс -execдействием и +для эффективности.

Команда gzipподдерживает задание имен нескольких файлов для сжатия. Но эта findкоманда, хотя она работает хорошо и не будет медленной, если у вас много файлов, запускает gzipкоманду один раз для каждого файла:

find . \( -name \*.css -o -name \*.html \) -exec gzip -vk {} \;

Это работает, и вы определенно можете его использовать. ( .выполняет поиск в текущем каталоге. Кроме того, это действительно немного другой способ написания команды в очень хорошем ответе Steeldriver ; вы можете использовать любой стиль, который вы предпочитаете.)

Вы также можете findпередать несколько имен файлов gzipи запускать их только столько раз, сколько необходимо - что почти всегда только один раз. Для этого используйте +вместо\; . +Аргумент должен прийти только после того, как {}. findзаменяет +дополнительные имена файлов, если таковые имеются.

find . \( -name \*.css -o -name \*.html \) -exec gzip -vk {} +

Его можно использовать, +даже если есть только несколько подходящих файлов, и когда их много, это может быть заметно быстрее, чем отдельный gzipвызов для каждого файла.

Как упоминает Steeldriver , вы можете использовать -inameвместо того, -nameчтобы сопоставлять файлы, имена которых заканчиваются на .cssили, .htmlно с другой прописной буквой . Это соответствует включению nocaseglobв globstarоснованном на методе, описанном выше.

Наконец, у вас, вероятно, нет соответствующих файлов или каталогов, которые начинаются с .. Но если вы делаете, findавтоматически включает их. Если вы хотите исключить их (как это происходит с globstarметодом -base, подробно описанным выше, когда dotglobон выключен), вы можете :

find . -not -path '*/.*' \( -name \*.css -o -name \*.html \) -exec gzip -vk {} +

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

Что не надо делать ...

Имена файлов могут содержать любой символ, кроме разделителя пути /и нулевого символа . Существует много техник, которые ломают странные имена файлов, и они обычно более сложны, чем техники, которые всегда работают. Поэтому я предлагаю избегать их, даже если вы знаете (или думаете, что знаете), что они в вашей конкретной ситуации. И, конечно, вы не должны использовать их, если у вас могут быть имена файлов с символами, которые могут обрабатываться особым образом, включая пробелы.

Можно безопасно передать вывод findдругой команды, которая ее обрабатывает, если вы используете -print0или подобное действие, чтобы заставить его помещать нулевой символ между путями вместо новой строки , и не иначе. Имена файлов могут содержать символы новой строки (хотя я не рекомендую вам сознательно именовать файлы вместе с ними). findКоманда с -printдействием - включая команды найти без каких - либо явных действий, так как то -printпо умолчанию - не производит вывод , который можно безопасно свирель или иной не предусмотрен в другую команду , которая выполняет действие на файлах.

Результат, findполученный с -print0действием, может быть безопасно передан по каналу xargs -0( -0флаг указывает xargsожидать ввода с нулевым разделением).

Элия ​​Каган
источник
0

Чтобы рекурсивно сжать все файлы в папке / подпапке:

gzip -r `find . -type f -name "*.html"` 

Разархивировать:

gunzip -r `find . -type f -name "*.gz"` 
Naruto_Hokage
источник
Этот метод, основанный на подстановке команд , часто ломается, и очень плохо. Проблема заключается в том, что имена файлов, содержащие пробелы или другие пробелы, будут разделены и обработаны как несколько имен файлов. (Эти команды написаны с использованием ` `синтаксиса, но проблема полностью применима и при использовании $( )синтаксиса.)
Элия ​​Каган