Làm thế nào để tôi tar một thư mục của các tập tin và thư mục mà không bao gồm chính thư mục?


382

Tôi thường làm:

tar -czvf my_directory.tar.gz my_directory

Điều gì sẽ xảy ra nếu tôi chỉ muốn bao gồm mọi thứ (bao gồm mọi tệp hệ thống ẩn) trong my_directory, nhưng không phải chính thư mục? Tôi không muốn:

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

Tôi muốn:

my_file
my_file
my_file

Đó có phải là hành vi mặc định của việc làm tar -czf? Trong trường hợp của tôi, nó chỉ lưu trữ các tập tin và không lưu trữ thư mục. Khi tôi chỉ tarthư mục bao gồm nó nhưng với tar -czfnó chỉ là thêm các tập tin.
Durga Swaroop

Câu trả lời:


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

nên làm công việc trong một dòng. Nó hoạt động tốt cho các tập tin ẩn là tốt. "*" không mở rộng các tệp ẩn bằng cách mở rộng tên đường dẫn ít nhất là trong bash. Dưới đây là thí nghiệm của tôi:

$ 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

2
Điều này cũng sẽ làm việc trên các tệp có khoảng trắng hoặc các ký tự đặc biệt khác. Làm tốt lắm!
PanCrit

29
Không hoàn hảo - tệp tar chứa '.' và cũng ./file1 thay vì chỉ file1. Tôi thích giải pháp của mateusza dưới đây để sử dụng --strip-thành phần khi bỏ tarring.
Ivan

23
@Ivan nếu bạn thay thế .bằng *lệnh cd my_directory/ && tar -zcvf ../my_dir.tgz * && cd ..thì nó sẽ hoạt động như bạn mong đợi.
Ẩn danh

3
@jmathew Bạn cũng có thể sử dụng một subshell để thư mục làm việc của shell hiện tại của bạn không thay đổi:$ (cd my_directory/ && tar -zcvf ../my_dir.tgz .)
Alec

Tôi biết đó là một câu trả lời cũ nhưng cdđi vào thư mục và ra là khá khập khiễng. Ít nhất có thể sử dụng pushdpopdnếu tarkhông có bất kỳ cờ như thế nào -C.
Andris

667

Sử dụng công -Ctắc của tar:

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

Thông báo -C my_directorytar để thay đổi thư mục hiện tại thành my_directory, và sau đó .có nghĩa là "thêm toàn bộ thư mục hiện tại" (bao gồm các tệp ẩn và thư mục con).

Hãy chắc chắn rằng bạn làm -C my_directorytrước khi bạn làm .nếu không bạn sẽ nhận được các tệp trong thư mục hiện tại.


5
+1 cảm ơn bạn! Đó là cái chết tiệt '.' Tôi đã mất tích. quá nặng nề
JC Bông

14
"Không giống như hầu hết các tùy chọn, -C được xử lý tại điểm nó xuất hiện trong danh sách các tệp sẽ được xử lý. Hãy xem xét lệnh sau: tar --create --file=foo.tar -C /etc passwd hosts -C /lib libc.a" apl.jhu.edu/Misc/Unix-info/tar/tar_65.html Tôi luôn thử tar -czvf my_directory.tar.gz * -C my_directoryvà Điều đó không làm việc. -Cvị trí rất quan trọng! Chết tiệt ...
m-ric

57
Không hoàn hảo - tệp tar chứa '.' và cũng ./file1 thay vì chỉ file1. Tôi thích giải pháp của mateusza dưới đây để sử dụng --strip-thành phần khi bỏ tarring.
Ivan

3
@Superole: shell thay thế các ký tự đại diện trước khi chạy tar. Cũng lưu ý rằng việc sử dụng ký tự đại diện *sẽ không bao gồm các tệp ẩn (đó là yêu cầu ban đầu).
dubek

28
Nó tạo ra. như một thư mục gốc trong .tar.gz.
Ẩn danh

59

Bạn cũng có thể tạo tệp lưu trữ như bình thường và giải nén nó bằng:

tar --strip-components 1 -xvf my_directory.tar.gz

3
Giải pháp này đặc biệt tốt trong các tình huống bạn đang làm việc với tarball được tạo trước khi tất cả các yêu cầu của bạn được biết đến ...
Digger

2
Lưu ý rằng đó --strip-componentslà một phần mở rộng GNU.
zneak

2
Câu trả lời này có thể được cải thiện bằng cách cung cấp ví dụ "như bình thường" trong ngữ cảnh.
Josh Habdas

33

Hãy xem --transform/ --xform, nó cung cấp cho bạn cơ hội để xoa bóp tên tệp khi tệp được thêm vào kho lưu trữ:

% 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

Biểu thức biến đổi tương tự như biểu thức sedvà chúng ta có thể sử dụng các dấu phân tách khác /( ,trong ví dụ trên).
https://www.gnu.org/software/tar/manual/html_section/tar_52.html


3
Tôi sẽ làm điều này. Bất cứ điều gì khác chỉ là một hack!
jwg

2
Đây là một giải pháp tốt hơn nhiều.
leesei

Đây là giải pháp tốt nhất.
vừa rồi

1
Giải pháp tốt, nhưng nó có thể gây ra file list too long. Giải pháp của tôi ngăn chặn điều đó và cũng linh hoạt hơn.
vào

Đây là một giải pháp tuyệt vời. Bạn cũng có thể vượt qua --xformnhiều lần cho nhiều đường dẫn.
elig

26

TL; DR

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

Với một số điều kiện (chỉ lưu trữ tệp, thư mục và liên kết tượng trưng):

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

Giải trình

Không may dưới đây bao gồm một thư mục cha ./trong kho lưu trữ:

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

Bạn có thể di chuyển tất cả các tệp ra khỏi thư mục đó bằng cách sử dụng --transformtùy chọn cấu hình, nhưng điều đó không thoát khỏi .chính thư mục đó. Nó trở nên ngày càng khó khăn để chế ngự lệnh.

Bạn có thể sử dụng $(find ...)để thêm danh sách tệp vào lệnh (như trong câu trả lời của Magnus ), nhưng điều đó có khả năng gây ra lỗi "danh sách tệp quá dài". Cách tốt nhất là kết hợp nó với -Ttùy chọn của tar , như thế này:

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

Về cơ bản những gì nó làm là liệt kê tất cả các tệp ( -type f), links ( -type l) và thư mục con ( -type d) trong thư mục của bạn, tạo tất cả tên tệp tương đối bằng cách sử dụng -printf "%P\n", sau đó chuyển nó sang lệnh tar (nó lấy tên tệp từ STDIN bằng cách sử dụng -T -). Các -Ctùy chọn là cần thiết để tar biết nơi các tập tin với những cái tên tương đối được đặt. Các --no-recursionlá cờ rất tar mà không recurse vào thư mục nó được nói đến lưu trữ (gây file trùng lặp).

Nếu bạn cần làm một cái gì đó đặc biệt với tên tệp (lọc, theo các liên kết tượng trưng, ​​v.v.), findlệnh này khá mạnh mẽ và bạn có thể kiểm tra nó bằng cách chỉ cần loại bỏ tarmột phần của lệnh trên:

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

Ví dụ: nếu bạn muốn lọc các tệp PDF, hãy thêm ! -name '*.pdf'

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

Không tìm thấy GNU

Lệnh sử dụng printf(có sẵn trong GNU find) findđể in kết quả của nó với các đường dẫn tương đối. Tuy nhiên, nếu bạn không có GNU find, điều này hoạt động để làm cho các đường dẫn tương đối (loại bỏ cha mẹ với 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 -

2
Câu trả lời chính xác. Rất công phu, và quan trọng nhất, giải quyết vấn đề một cách hoàn hảo.
Alex

16

Câu trả lời này sẽ hoạt động trong hầu hết các tình huống. Tuy nhiên, lưu ý cách tên tệp được lưu trữ trong tệp tar, ví dụ, ./file1thay vì chỉ file1. Tôi thấy rằng điều này gây ra vấn đề khi sử dụng phương pháp này để thao tác các tarball được sử dụng làm tệp gói trong BuildRoot .

Một giải pháp là sử dụng một số thông tin của Bash để liệt kê tất cả các tệp ngoại trừ ..như thế này:

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

Đây là một mẹo tôi học được từ câu trả lời này .

Bây giờ tar sẽ trả về một lỗi nếu không có tệp nào khớp ..?*hoặc .[^.]*, nhưng nó vẫn hoạt động. Nếu lỗi là một vấn đề (bạn đang kiểm tra thành công trong một tập lệnh), thì điều này hoạt động:

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

Mặc dù bây giờ chúng tôi đang gặp rắc rối với các tùy chọn shell, chúng tôi có thể quyết định rằng nó *phù hợp hơn với các tệp ẩn phù hợp:

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

Điều này có thể không hoạt động khi vỏ của bạn ảm đạm *trong thư mục hiện tại, do đó, thay vào đó, sử dụng:

shopt -s dotglob
cd my_dir
tar -zcvf ../my_dir.tar.gz *
cd ..
shopt -u dotglob

1
Tôi nhận được lỗi wierd khi tôi làm điều này tar: start.sh: Cannot stat: No such file or directoryĐiều này xảy ra với tất cả các tệp trong thư mục hiện tại của tôi! Làm thế nào để tôi tránh điều này?
BrainStone

@BrainStone Tôi nhận được kết quả chính xác như nhau.
mbmast

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

2
Hal hỏi rõ ràng về các tập tin ẩn. Bạn cũng cần. ?? *.
PanCrit

2
-1: Điều này không thêm các tập tin ẩn vào tar. Xem câu trả lời của tbman.
dubek

3

Nếu đó là hệ thống Unix / Linux và bạn quan tâm đến các tệp ẩn (sẽ bị bỏ qua bởi *), bạn cần phải làm:

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

Tôi không biết các tập tin ẩn trông như thế nào trong Windows.


2

Tôi sẽ đề xuất hàm Bash sau (đối số đầu tiên là đường dẫn đến thư mục, đối số thứ hai là tên cơ sở của lưu trữ kết quả):

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"
}

Bạn có thể chạy nó theo cách này:

$ tar_dir_contents /path/to/some/dir my_archive

và nó sẽ tạo ra các kho lưu trữ my_archive.tar.gztrong thư mục hiện tại. Nó hoạt động với các phần tử ẩn (. *) Và với các phần tử có khoảng trắng trong tên tệp của chúng.


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

Cái này hoạt động với tôi và nó bao gồm tất cả các tệp ẩn mà không đặt tất cả các tệp vào thư mục gốc có tên "." giống như trong câu trả lời của tomoe :


1
tar.create() {
        local folder="${1}"

        local tar="$(basename "${folder}")".tar.gz

        cd "${folder}" && tar -zcvf "../${tar}" .; cd - &> /dev/null
}

Thí dụ:

tar.create folder

Không có gì.


1

Đây là những gì làm việc cho tôi.

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

Bạn cũng có thể sử dụng

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

Trong lệnh đầu tiên, find trả về một danh sách các tệp và thư mục con của my_dir . Tuy nhiên, thư mục my_dir tự nó được bao gồm trong danh sách đó là '.' Các -printf tham số loại bỏ các đường dẫn đầy đủ trong đó có '' và tất cả Tuy nhiên trong chuỗi định dạng '% P' của printf để lại phần còn lại trong danh sách các tệp và thư mục con của my_dir và có thể được nhìn thấy bởi một khoảng trắng hàng đầu trong kết quả của lệnh find .

Điều đó sẽ không thành vấn đề đối với TAR nhưng nếu bạn muốn khắc phục điều này, hãy thêm phần 1 như trong lệnh thứ hai.


0

Sử dụng pax.

Pax là một gói không dùng nữa nhưng thực hiện công việc một cách hoàn hảo và đơn giản.

pax -w > mydir.tar mydir

Thực tế nhất và thực hiện công việc +1
Breno Salgado

lệnh này tạo mydir.tar với nội dung: mydir / file1 mydir / file2, chính xác là những gì cần tránh.
Alex

0

Cách đơn giản nhất tôi tìm thấy:

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


1
Điều đó sẽ không bao gồm các tập tin ẩn.
asynts

0
# 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=-
}

Xử lý an toàn tên tệp có dấu cách hoặc ký tự bất thường khác. Bạn có thể tùy ý thêm một -name '*.sql'hoặc bộ lọc tương tự vào lệnh find để giới hạn các tệp được bao gồm.


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

Nếu thư mục là mytemp thì nếu bạn áp dụng ở trên, nó sẽ nén và xóa tất cả các tệp trong thư mục nhưng để yên

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

Bạn có thể đưa ra các mẫu loại trừ và cũng chỉ định không nhìn vào các thư mục con


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.