Как я могу архивировать каталог файлов и папок, не включая сам каталог?

Обычно я делаю:

tar -czvf my_directory.tar.gz my_directory

Что, если я просто хочу включить все (включая любые скрытые системные файлы) в my_directory, но не сам каталог? Я не хочу:

my_directory
   --- my_file
   --- my_file
   --- my_file

Я хочу:

my_file
my_file
my_file

person Community    schedule 02.06.2009    source источник
comment
Это поведение по умолчанию - выполнение tar -czf? В моем случае это только файлы, а не каталог. Когда я просто tar каталог, он включает его, но с tar -czf он только добавляет файлы.   -  person Durga Swaroop    schedule 05.01.2017


Ответы (19)


cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd - 

должен выполнять работу в одну линию. Он также хорошо работает со скрытыми файлами. «*» не расширяет скрытые файлы путем раскрытия имени пути, по крайней мере, в bash. Ниже мой эксперимент:

$ mkdir my_directory
$ touch my_directory/file1
$ touch my_directory/file2
$ touch my_directory/.hiddenfile1
$ touch my_directory/.hiddenfile2
$ cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd ..
./
./file1
./file2
./.hiddenfile1
./.hiddenfile2
$ tar ztf my_dir.tgz
./
./file1
./file2
./.hiddenfile1
./.hiddenfile2
person tomoe    schedule 03.06.2009
comment
Это также будет работать с файлами с пробелами или другими специальными символами. Молодец! - person PanCrit; 04.06.2009
comment
Не идеально - tar-файл содержит '.' а также ./file1 вместо только file1. Мне нравится решение mateusza ниже использовать --strip-components при разархивировании. - person Ivan; 04.04.2013
comment
@Ivan, если вы замените . на *, чтобы команда была cd my_directory/ && tar -zcvf ../my_dir.tgz * && cd .., она будет работать так, как вы ожидали. - person Anonymous; 03.03.2015
comment
@jmathew Вы также можете использовать подоболочку, чтобы рабочий каталог вашей текущей оболочки не изменился: $ (cd my_directory/ && tar -zcvf ../my_dir.tgz .) - person Alec; 31.08.2017
comment
Я знаю, что это старый ответ, но cd заходить в каталоги и выходить из них довольно неубедительно. Можно было бы хотя бы использовать pushd и popd, если бы tar не было таких флагов, как -C. - person Andris; 19.02.2019
comment
Кто-нибудь знает, почему это так сложно? Похоже, создатели tar сильно упустили ... - person information_interchange; 22.03.2021
comment
в текущей Ubuntu это дает мне архив с папкой с именем «.» в этом. - person Caldera; 25.04.2021

Используйте переключатель -C в tar:

tar -czvf my_directory.tar.gz -C my_directory .

-C my_directory указывает tar изменить текущий каталог на my_directory, а затем . означает «добавить весь текущий каталог» (включая скрытые файлы и подкаталоги).

Убедитесь, что вы выполнили -C my_directory, прежде чем делать ., иначе вы получите файлы в текущем каталоге.

person dubek    schedule 14.06.2010
comment
+1 спасибо! Это было чертовски ». Я пропал. так обидно - person JCotton; 05.05.2011
comment
В отличие от большинства опций, -C обрабатывается в том месте, где она встречается в списке файлов, которые нужно обработать. Рассмотрим следующую команду: tar --create --file=foo.tar -C /etc passwd hosts -C /lib libc.a apl.jhu.edu/Misc/ Unix-info / tar / tar_65.html Я всегда пробую tar -czvf my_directory.tar.gz * -C my_directory, и это не работает. -C расположение важно! Проклятая смола ... - person m-ric; 04.01.2013
comment
Не идеально - tar-файл содержит '.' а также ./file1 вместо только file1. Мне нравится решение mateusza ниже использовать --strip-components при разархивировании. - person Ivan; 04.04.2013
comment
как ни странно -C не работает в сочетании с подстановочными знаками. Он перемещается в real текущий каталог, а затем пытается найти файлы в текущем каталоге tar. tar cf foo.tar -C /lib lib* приводит к tar: lib*: Cannot stat: No such file or directory - person Superole; 27.05.2013
comment
@Superole: shell подставляет подстановочные знаки перед запуском tar. Также обратите внимание, что использование подстановочного знака, такого как *, не будет включать скрытые файлы (что было исходным требованием). - person dubek; 09.06.2013
comment
Я не знал о. чтобы добавить весь каталог. Нравится рецепт «два в одном» от дубека! - person tuk0z; 13.12.2014
comment
Это особенно полезно, когда вы используете tar вместо cp. - person David Ehrmann; 21.01.2015
comment
Это создает . в качестве корневого каталога в .tar.gz. - person Anonymous; 03.03.2015
comment
Для тех, кому не нужен . родительский каталог, вот решение - person aross; 08.03.2017

Вы также можете создать архив как обычно и извлечь его с помощью:

tar --strip-components 1 -xvf my_directory.tar.gz
person mateusza    schedule 02.06.2009
comment
Это решение особенно хорошо в ситуациях, когда вы работаете с архивами, созданными до того, как все ваши требования были известны ... - person Digger; 22.09.2016
comment
Имейте в виду, что --strip-components - это расширение GNU. - person zneak; 14.08.2018
comment
Этот ответ можно улучшить, приведя обычный пример в контексте. - person Josh Habdas; 23.03.2019

Взгляните на _1 _ / _ 2_, он дает вам возможность изменить имя файла при добавлении файла в архив:

% mkdir my_directory
% touch my_directory/file1
% touch my_directory/file2
% touch my_directory/.hiddenfile1
% touch my_directory/.hiddenfile2
% tar -v -c -f my_dir.tgz --xform='s,my_directory/,,' $(find my_directory -type f)
my_directory/file2
my_directory/.hiddenfile1
my_directory/.hiddenfile2
my_directory/file1
% tar -t -f my_dir.tgz 
file2
.hiddenfile1
.hiddenfile2
file1

Выражение преобразования похоже на выражение sed, и мы можем использовать разделители, отличные от / (, в приведенном выше примере).
https://www.gnu.org/software/tar/manual/html_section/tar_52.html

person Magnus    schedule 30.10.2013
comment
Я бы сделал это. Все остальное - это просто взлом! - person jwg; 10.08.2015
comment
Это гораздо лучшее решение. - person leesei; 16.05.2016
comment
Это лучшее решение. - person justhalf; 14.12.2016
comment
Хорошее решение, но может вызвать file list too long. Мое решение предотвращает это и к тому же является более гибким. - person aross; 08.03.2017
comment
Это отличное решение. Вы также можете передать --xform несколько раз для нескольких путей. - person elig; 02.01.2020

TL; DR (нет ./ и нет ./file1!)

find /my/dir/ -printf "%P\n" | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

С некоторыми условиями (архивировать только файлы, каталоги и символические ссылки):

find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

Объяснение

Ниже, к сожалению, есть родительский каталог ./ в архиве:

tar -czf mydir.tgz -C /my/dir .

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

Вы можете использовать $(find ...) для добавления списка файлов к команде (например, в ответе magnus), но это потенциально может вызвать файл список слишком длинный ошибка. Лучше всего комбинировать его с параметром tar -T, например:

find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

По сути, он перечисляет все файлы (-type f), ссылки (-type l) и подкаталоги (-type d) в вашем каталоге, делает все имена файлов относительными с помощью -printf "%P\n", а затем передает это команде tar (имена файлов принимаются из STDIN с использованием -T -). Параметр -C необходим, чтобы tar знал, где находятся файлы с относительными именами. Флаг --no-recursion предназначен для того, чтобы tar не возвращался в папки, которые он должен архивировать (вызывая дублирование файлов).

Если вам нужно сделать что-то особенное с именами файлов (фильтрация, отслеживание символических ссылок и т. Д.), Команда find очень эффективна, и вы можете протестировать ее, просто удалив часть tar из приведенной выше команды:

$ find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d
> textfile.txt
> documentation.pdf
> subfolder2
> subfolder
> subfolder/.gitignore

Например, если вы хотите отфильтровать файлы PDF, добавьте ! -name '*.pdf'

$ find /my/dir/ -printf "%P\n" -type f ! -name '*.pdf' -o -type l -o -type d
> textfile.txt
> subfolder2
> subfolder
> subfolder/.gitignore

Не-GNU найти

В команде используется printf (доступно в GNU find), который сообщает find о необходимости распечатывать результаты с относительными путями. Однако, если у вас нет GNU find, это сработает, чтобы сделать пути относительными (удаляет родителей с sed):

find /my/dir/ -type f -o -type l -o -type d | sed s,^/my/dir/,, | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -
person aross    schedule 16.09.2016
comment
Отличный ответ. Очень проработано, а главное отлично решает проблему. - person Alex; 31.03.2017

Этот ответ должен работать в большинстве ситуаций. Обратите внимание, однако, как имена файлов хранятся в файле tar, например, ./file1, а не просто file1. Я обнаружил, что это вызывает проблемы при использовании этого метода для управления архивами, используемыми в качестве файлов пакетов в BuildRoot.

Одно из решений - использовать несколько команд Bash для вывода списка всех файлов, кроме .., например:

tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *

Этому трюку я научился из этого ответа.

Теперь tar вернет ошибку, если нет файлов, соответствующих ..?* или .[^.]*, но он все равно будет работать. Если ошибка является проблемой (вы проверяете успешность сценария), это работает:

shopt -s nullglob
tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *
shopt -u nullglob

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

shopt -s dotglob
tar -C my_dir -zcvf my_dir.tar.gz *
shopt -u dotglob

Это может не сработать, если ваша оболочка указывает * в текущем каталоге, поэтому в качестве альтернативы используйте:

shopt -s dotglob
cd my_dir
tar -zcvf ../my_dir.tar.gz *
cd ..
shopt -u dotglob
person Rob Fisher    schedule 19.03.2012
comment
Когда я делаю это, я получаю странные ошибки tar: start.sh: Cannot stat: No such file or directory Это происходит со всеми файлами в моем текущем каталоге! Как мне этого избежать? - person BrainStone; 04.10.2013
comment
@BrainStone Я получаю точно такие же результаты. - person mbmast; 21.12.2016

cd my_directory && tar -czvf ../my_directory.tar.gz $(ls -A) && cd ..

Этот сработал для меня, и он включает все скрытые файлы, не помещая все файлы в корневой каталог с именем "." как в ответе Томоэ:

person med    schedule 09.02.2017
comment
tar прервется, если ls файл или имя каталога содержат пробел. Другие причины, почему не использовать ls - person Jimmix; 02.03.2021

Если это система Unix / Linux, и вы заботитесь о скрытых файлах (которые будут пропущены *), вам необходимо сделать:

cd my_directory
tar zcvf ../my_directory.tar.gz * .??*

Я не знаю, как выглядят скрытые файлы под Windows.

person PanCrit    schedule 02.06.2009

Я бы предложил следующую функцию Bash (первый аргумент - это путь к каталогу, второй аргумент - это базовое имя результирующего архива):

function tar_dir_contents ()
{
    local DIRPATH="$1"
    local TARARCH="$2.tar.gz"
    local ORGIFS="$IFS"
    IFS=$'\n'
    tar -C "$DIRPATH" -czf "$TARARCH" $( ls -a "$DIRPATH" | grep -v '\(^\.$\)\|\(^\.\.$\)' )
    IFS="$ORGIFS"
}

Вы можете запустить его так:

$ tar_dir_contents /path/to/some/dir my_archive

и он сгенерирует архив my_archive.tar.gz в текущем каталоге. Он работает со скрытыми (. *) Элементами и с элементами, в имени файла которых есть пробелы.

person gpz500    schedule 27.02.2014
comment
Избегайте использования ls для этой ссылки - person Jimmix; 02.03.2021

Это то, что у меня работает.

tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -maxdepth 1 -printf '%P ')

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

tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -mindepth 1 -maxdepth 1 -printf '%P ')

В первой команде find возвращается список файлов и подкаталогов my_dir. Однако каталог my_dir сам включен в этот список как '.' Параметр -printf удаляет полный путь, включая "." а также все crlf Однако space в строке формата '% P' < / em> из printf оставляет остаток в списке файлов и подкаталогов my_dir и обозначается начальным пробелом в результате выполнения команды find.

Это не будет проблемой для TAR, но если вы хотите исправить это, добавьте -mindepth 1, как во второй команде.

person serendrewpity    schedule 16.05.2020

Используйте pax.

Pax является устаревшим пакетом, но выполняет свою работу безупречно и просто.

pax -w > mydir.tar mydir
person rjv    schedule 07.01.2013
comment
Самый практичный и выполняет свою работу +1 - person Breno Salgado; 24.07.2013
comment
эта команда создает mydir.tar с содержимым: mydir / file1 mydir / file2, именно этого следует избегать. - person Alex; 16.04.2016

Самый простой способ, который я нашел:

cd my_dir && tar -czvf ../my_dir.tar.gz *

person alexgo    schedule 16.05.2017
comment
Это не будет включать скрытые файлы. - person asynts; 26.06.2019

# tar all files within and deeper in a given directory
# with no prefixes ( neither <directory>/ nor ./ )
# parameters: <source directory> <target archive file>
function tar_all_in_dir {
    { cd "$1" && find -type f -print0; } \
    | cut --zero-terminated --characters=3- \
    | tar --create --file="$2" --directory="$1" --null --files-from=-
}

Безопасно обрабатывает имена файлов с пробелами и другими необычными символами. При желании вы можете добавить -name '*.sql' или аналогичный фильтр к команде find, чтобы ограничить количество включаемых файлов.

person marcingo    schedule 23.08.2016

function tar.create() {
        local folder="${1}"
        
        local tar="$(basename "${folder}")".tar.gz
        
        cd "${folder}" && tar -zcvf "../${tar}" .; cd - &> /dev/null
}

Пример:

tar.create /path/to/folder

Добро пожаловать.

person mmm    schedule 08.01.2020

Командование

find my_directory/ -maxdepth 1 -printf "%P\n" | tar -cvf my_archive.tar -C my_directory/ -T -

Это создает стандартный архивный файл. Стандартный способ, которым файлы и каталоги, которые вы хотите упаковать, находятся в корне архива. Нет никаких дополнительных специальных файлов, таких как '.' и dirs как ./ перед каждым './file'.
Пробуя так много решений, которые уже есть, только с одним успешно, мне кажется, что использование find или, возможно, других команд, таких как ls -A -1, в левой части канала (- 1 как «один», а не -l как «L») - единственный способ достичь вышеуказанной цели.

Если tar-файл в дальнейшем обрабатывается или доставляется кому-то в качестве продукта моей работы, я не хочу, чтобы там были какие-то странности.

Описание аргументов

-maxdepth 1
По убыванию не более чем на 1 уровень - без рекурсии.
-printf
вывести формат на стандартный вывод
%P Имя файла с именем начального - точка, ниже которой он был обнаружен, удален.
\n Новая строка
printf не добавляет новую строку в конец строки. Это должно быть добавлено сюда

tar:
-C DIR, --directory=DIR
перейти в каталог DIR

-T FILE, --files-from=FILE
получить имена для извлечения или создания из ФАЙЛА
-
этот ФАЙЛ сверху является стандартным вводом из канала


Комментарии к другим решениям.

Тот же результат может быть достигнут с помощью решения, описанного @aross.
Отличие от решения здесь в том, какой инструмент выполняет рекурсию. Если вы оставите задание find, каждое имя пути к файлу будет проходить через конвейер. Он также отправляет все имена каталогов, которые tar с параметром --no-recursion игнорирует или добавляет как пустые, за которыми следуют все файлы в каждом каталоге. Если при чтении файла из find произошел неожиданный вывод в виде ошибок, tar не узнает и не позаботится о том, что происходит.
Но с дальнейшими проверками, такими как обработка потока ошибок из find, это может быть хорошим решением, когда много опций и фильтров на файлах.
Я предпочитаю оставить рекурсию на tar, это кажется более простым и более стабильным решением.
С моей сложной структурой каталогов я уверен, что архив будет готов, когда tar будет не сообщать об ошибке.

Другое решение с использованием find, предложенное @serendrewpity, кажется прекрасным, но оно не работает с именами файлов с пробелами. Разница в том, что вывод find, предоставленный суб-оболочкой $ (), разделен пробелами. Можно было бы добавить кавычки с помощью printf, но это еще больше усложнило бы инструкцию.

Нет причин делать cd в my_directory, а затем обратно при использовании ../my_archive.tar в качестве пути tar, потому что TAR имеет команду -C DIR, --directory=DIR, которая существует только для этой цели.

Использование . (точка) будет включать точки

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

Этот последний пункт также относится ко всем решениям, в которых не используется pipe.

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

person papo    schedule 19.12.2020

cd DIRECTORY
tar -czf NAME.tar.gz  *

звездочка будет включать все, даже скрытые

person user2328973    schedule 05.02.2021

tar -czvf mydir.tgz -C my_dir/ `ls -A mydir`

Запустите его на один уровень выше mydir. Это не будет включать ни [.], Ни прочее.

person Airstriker    schedule 17.02.2021
comment
Это также не будет включать файлы / каталоги с пробелами. ссылка - person Jimmix; 02.03.2021

 tar -cvzf  tarlearn.tar.gz --remove-files mytemp/*

Если папка mytemp, то, если вы примените вышеуказанное, она будет заархивирована и удалит все файлы в папке, но оставит ее в покое.

 tar -cvzf  tarlearn.tar.gz --remove-files --exclude='*12_2008*' --no-recursion mytemp/*

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

person user1456599    schedule 13.02.2013

person    schedule
comment
Хэл прямо спросил о скрытых файлах. Вам также понадобится. ?? *. - person PanCrit; 02.06.2009
comment
-1: Это не добавляет скрытые файлы в tar. См. Ответ tbman. - person dubek; 14.06.2010