summaryrefslogtreecommitdiff
path: root/doc/recipes.texi
blob: d4def4a92fec5884492fde287ef97ab8d1f83044 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
@c This is part of the GNU tar manual.
@c Copyright (C) 2017-2019 Free Software Foundation, Inc.
@c This file is distributed under GFDL 1.3 or any later version
@c published by the Free Software Foundation.

This appendix provides several recipes for performing common tasks
using @GNUTAR{}.

@menu
* copy directory hierarchy::
* intermediate directories::
@end menu

@node copy directory hierarchy
@appendixsec Copying directory hierarchies

This is a traditional way to copy a directory hierarchy preserving
the dates, modes, owners and link-structure of all the files therein.
It was used back when the @command{cp} command lacked the @option{-a}
option:

@smallexample
$ @kbd{(cd sourcedir; tar -cf - .) | (cd targetdir; tar -xf -)}
@end smallexample

@noindent
You can avoid subshells by using @option{-C} option:

@smallexample
$ @kbd{tar -C sourcedir -cf - . | tar -C targetdir -xf -}
@end smallexample

@noindent
The same command using long option forms:

@smallexample
@group
$ @kbd{(cd sourcedir; tar --create --file=- . ) \
       | (cd targetdir; tar --extract --file=-)}
@end group
@end smallexample

@noindent
or

@smallexample
@group
$ @kbd{tar --directory sourcedir --create --file=- . \
       | tar --directory targetdir --extract --file=-}
@end group
@end smallexample

@node intermediate directories
@appendixsec Restoring Intermediate Directories

A common concern is how to extract permissions and ownerships of
intermediate directories when extracting only selected members from
the archive.  To illustrate this, consider the following archive:

@example
@group
# tar tvf A.tar
drwxr-xr-x root/root         0 2017-11-16 14:39 foo/
dr-xr-x--- gray/user         0 2017-11-16 14:39 foo/bar/
-rw-r--r-- gray/user        10 2017-11-16 14:40 foo/bar/file
@end group
@end example

Suppose you extract only the file @file{foo/bar/file}, while being
@samp{root}:

@example
# @kbd{tar xvf A.tar foo/bar/file}
foo/bar/file
@end example

Now, let's inspect the content of the created directories:

@example
@group
# find foo -ls
427257    0 drwxr-xr-x   3 root     root    16 Nov 17 16:10 foo
427258    0 drwxr-xr-x   2 root     root    17 Nov 17 16:10 foo/bar
427259    0 -rw-r--r--   1 gray     user    10 Nov  6 14:40 foo/bar/file
@end group
@end example

The requested file is restored, including its ownership and
permissions. The intermediate directories, however, are created with
the default permissions, current timestamp and owned by the current
user. This is because by the time @command{tar} has reached the requested file,
it had already skipped the entries for its parent directories, so it
has no iformation about their ownership and modes.

To restore meta information about the intermediate directories,
you'll need to specify them explicitly in the command line and use the
@option{--no-recursive} option (@pxref{recurse}) to avoid extracting
their content.

To automate this process, @cite{Neal P. Murphy} proposed the following
shell script@footnote{The original version of the script can be
seen at @uref{http://lists.gnu.org/archive/html/bug-tar/2016-11/msg00024.html}}:

@example
@group
#! /bin/sh
(while read path
 do
   path=`dirname $path`
   while [ -n "$path" -a "$path" != "." ]
   do
     echo $path
     path=`dirname $path`
   done
 done < $2 | sort | uniq) |
 tar -x --no-recursion -v -f $1 -T - -T $2
@end group
@end example

The script takes two arguments: the name of the archive file, and the
name of the file list file.

To complete our example, the file list will contain single line:

@example
foo/bar/file
@end example

Supposing its name is @file{file.list} and the script is named
@file{restore.sh}, you can invoke it as follows: 

@example
# @kbd{sh restore.sh A.tar file.list}
@end example