Điều này sẽ giúp xác định những gì đang diễn ra trong câu trả lời của Johnny , cũng như trả lời câu hỏi tại sao điều này hoạt động trên Linux mà không phải là Mac.
Vấn đề nằm ở chỗ Mac OS X sử dụng bsdtar
, trong khi hầu hết các hệ thống Linux đều sử dụng gnutar
.
Bạn có thể cài đặt gnutar
trên máy Mac với Homebrew, bằng cách sử dụng brew install gnu-tar
, sẽ liên kết gnutar
với nhau /usr/local/bin
dưới dạng gtar
.
Nếu bạn cài đặt gnutar
, thì bạn có thể tái tạo vấn đề bằng các bước trong câu trả lời của Johnny .
$ brew install gnu-tar
==> Downloading https://homebrew.bintray.com/bottles/gnu-tar-1.28.yosemite.bottle.2.tar.gz
######################################################################## 100.0%
==> Pouring gnu-tar-1.28.yosemite.bottle.2.tar.gz
==> Caveats
gnu-tar has been installed as "gtar".
If you really need to use it as "tar", you can add a "gnubin" directory
to your PATH from your bashrc like:
PATH="/usr/local/opt/gnu-tar/libexec/gnubin:$PATH"
==> Summary
🍺 /usr/local/Cellar/gnu-tar/1.28: 13 files, 1.6M
$ mkdir test
$ touch test/a test/b
$ gtar -zcvf test.tar.gz test test/a # make the archive with gnutar
test/
test/a
test/b
test/a
$ gtar -ztvf test.tar.gz
drwxr-xr-x adamliter/staff 0 2015-07-28 22:41 test/
-rw-r--r-- adamliter/staff 0 2015-07-28 22:41 test/a
-rw-r--r-- adamliter/staff 0 2015-07-28 22:41 test/b
hrw-r--r-- adamliter/staff 0 2015-07-28 22:41 test/a link to test/a
$ rm -r test
$ tar -xvf test.tar.gz # try to unpack the archive with bsdtar
x test/
x test/a
x test/b
x test/a: Can't create 'test/a'
tar: Error exit delayed from previous errors.
$ echo $?
1
Vì vậy, rõ ràng gnutar
lưu trữ những thứ khác nhau theo cách gây ra bsdtar
nghẹt thở trên các bản sao. Thực tế cho thấy gtar -ztvf test.tar.gz
rằng phiên bản thứ hai test/a
được lưu trữ dưới dạng link to test/a
có liên quan. Như Johnny chỉ ra trong các bình luận, gnutar
sẽ lưu trữ các bản sao dưới dạng liên kết cứng thay vì tệp thực tế, có thể bị vô hiệu hóa --hard-dereference
.
Đó là, bạn có thể làm như sau:
$ mkdir test
$ touch test/a test/b
$ gtar -zcvf test.tar.gz test test/a --hard-dereference
test/
test/a
test/b
test/a
$ gtar -ztvf test.tar.gz test
drwxr-xr-x adamliter/staff 0 2015-07-28 23:49 test/
-rw-r--r-- adamliter/staff 0 2015-07-28 23:49 test/a
-rw-r--r-- adamliter/staff 0 2015-07-28 23:49 test/b
-rw-r--r-- adamliter/staff 0 2015-07-28 23:49 test/a # note that this is no longer a link
$ rm -r test
$ tar -xvf test.tar.gz # unpack with bsdtar
x test/
x test/a
x test/b
x test/a
$ echo $?
0
$ ls test/
a b
Tuy nhiên, trong trường hợp này, rõ ràng bạn không kiểm soát việc tạo tarball, vì vậy --hard-dereference
không phải là một lựa chọn. May mắn thay, dựa trên câu trả lời của OP , có vẻ như vấn đề này đã được khắc phục bằng cách ngược dòng.
Tuy nhiên, nếu bất kỳ ai khác gặp phải vấn đề này trong tương lai và cần khắc phục nhanh hoặc có một người bảo trì ngược dòng không phản hồi, có một cách giải quyết.
Khi bạn xác định tệp trùng lặp là gì, bạn có thể sử dụng --fast-read
tùy chọn bsdtar
(lưu ý rằng tùy chọn này chỉ là một phần của bsdtar
, không phải gnutar
):
-q (--fast-read)
(x and t mode only) Extract or list only the first archive entry that matches each pattern or filename operand. Exit as soon as each specified pat-
tern or filename has been matched. By default, the archive is always read to the very end, since there can be multiple entries with the same name
and, by convention, later entries overwrite earlier entries. This option is provided as a performance optimization.
Vì vậy, trong ví dụ về đồ chơi mà tôi đã tạo theo ví dụ về đồ chơi trong câu trả lời của Johnny , tệp trùng lặp là test/a
. Vì vậy, bạn có thể tránh vấn đề này bằng cách làm như sau:
# this set of commands picks up from the first set of commands
# i.e., the following assumes a tarball that was *not* made with
# the --hard-dereference option, although this will work just as well
# with one that was
$ tar -xvqf test.tar.gz test/a # unarchive the first instance of test/a
x test/a
$ tar -xvf test.tar.gz --exclude test/a # unarchive everything except test/a
x test/
x test/b
$ echo $?
0
$ ls test/
a b
Lưu ý, hơn nữa, điều đó gnutar
hoàn toàn vui khi giải nén một kho lưu trữ với các bản sao được tạo bởi chính nó, ngay cả khi --hard-dereference
tùy chọn không được sử dụng:
$ rm -r test
$ gtar -xvf test.tar.gz
test/
test/a
test/b
test/a
$ echo $?
0
$ ls test/
a b
Vì vậy, điều này trả lời câu hỏi của bạn về lý do tại sao một lỗi được ném trên Mac mà không phải là Linux. (Hầu hết) Linux phân phối cùng với gnutar
, và vì tarball có lẽ được đóng gói cùng gnutar
, nên sẽ không có lỗi khi giải nén gnutar
, nhưng sẽ có lỗi khi giải nén bsdtar
.
Để đọc thêm và tham khảo, người ta có thể muốn xem Sự khác biệt giữa bsdtar và GNU tar là gì? trên Unix.SE.