Как заархивировать каталог файлов и папок, не включая сам каталог?
Вопрос
Обычно я делаю:
tar -czvf my_directory.tar.gz my_directory
Что, если я просто хочу включить все (включая любые скрытые системные файлы) в my_directory, но не сам каталог?Я не хочу:
my_directory
--- my_file
--- my_file
--- my_file
Я хочу:
my_file
my_file
my_file
Решение
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
Другие советы
Использовать -C
переключатель смолы:
tar -czvf my_directory.tar.gz -C my_directory .
А -C my_directory
сообщает tar изменить текущий каталог на my_directory
, а потом .
означает «добавить весь текущий каталог» (включая скрытые файлы и подкаталоги).
Убедитесь, что вы делаете -C my_directory
прежде чем ты это сделаешь .
иначе вы получите файлы в текущем каталоге.
Вы также можете создать архив как обычно и извлечь его с помощью:
tar --strip-components 1 -xvf my_directory.tar.gz
Посмотри на --transform
/--xform
, это дает вам возможность изменять имя файла при добавлении файла в архив:
% 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
ТЛ;ДР
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 ...)
чтобы добавить список файлов в команду (как в ответ Магнуса), но это потенциально может привести к ошибке «слишком длинный список файлов».Лучший способ - объединить его с 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 -
Этот ответ должно работать в большинстве ситуаций.Однако обратите внимание, как имена файлов хранятся в файле tar, например: ./file1
а не просто file1
.Я обнаружил, что это вызывает проблемы при использовании этого метода для управления архивами, используемыми в качестве файлов пакетов в БилдРут.
Одним из решений является использование некоторых glob 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
cd my_directory
tar zcvf ../my_directory.tar.gz *
Если это система Unix/Linux и вас интересуют скрытые файлы (которые будут пропущены знаком *), вам нужно сделать:
cd my_directory
tar zcvf ../my_directory.tar.gz * .??*
Я не знаю, как выглядят скрытые файлы в Windows.
Я бы предложил следующую функцию 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
внутри текущего каталога.Он работает со скрытыми (.*) элементами и с элементами с пробелами в имени файла.
cd my_directory && tar -czvf ../my_directory.tar.gz $(ls -A) && cd ..
Этот сработал для меня, и он включает все скрытые файлы, не помещая все файлы в корневой каталог с именем "". как в ответ Томоэ :
Используйте Пакс.
Pax — устаревший пакет, но он отлично и просто выполняет свою работу.
pax -w > mydir.tar mydir
Самый простой способ, который я нашел:
cd my_dir && tar -czvf ../my_dir.tar.gz *
# 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, чтобы ограничить количество включенных файлов.
tar -cvzf tarlearn.tar.gz --remove-files mytemp/*
Если это папка mytemp, то если вы примените вышеуказанное, она заархивируется и удалит все файлы в папке, но оставит ее в покое.
tar -cvzf tarlearn.tar.gz --remove-files --exclude='*12_2008*' --no-recursion mytemp/*
Вы можете указать шаблоны исключения, а также указать, чтобы не просматривать подпапки.
tar -C my_dir -zcvf my_dir.tar.gz `ls my_dir`