Đ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 gnutartrên máy Mac với Homebrew, bằng cách sử dụng brew install gnu-tar, sẽ liên kết gnutarvới nhau /usr/local/bindướ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 gnutarlưu trữ những thứ khác nhau theo cách gây ra bsdtarnghẹt thở trên các bản sao. Thực tế cho thấy gtar -ztvf test.tar.gzrằng phiên bản thứ hai test/ađược lưu trữ dưới dạng link to test/acó liên quan. Như Johnny chỉ ra trong các bình luận, gnutarsẽ 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-dereferencekhô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-readtù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 đó gnutarhoà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-dereferencetù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.