I've been in the same boat. tar is so bafflingly underdocumented for a de-facto standard utility! Most of us do not have Dan around, so unfortunately...
My use case involves tar archives, some of which produced by software out of my control, and the task at hand is extract the top-level directory named either './opt/' or 'opt/'. This is part of an automated VM imaging process, that I obviously want to work robustly.
What I came up with is far from a general solution, but does the thing I need, in my limited scope. It won't generalize well if you need to extract a large number of top-level directories, and dealing with cases where the names of the top-level directories are not known in advance is tricky.
GNU tar has a --transform=
option that takes a sed s
operator, but unfortunately, command-line pattern match is performed before the transform is applied. However, building upon @user9517's answer which I gladly upvote, I came up with the following elegant solution horrible hack that does the job anyway. The idea is to both match opt/
anywhere in path and use the transform to redirect files that happen to spuriously match opt/
anywhere except the top level into a temporary path with a transform, to be deleted after extracting. The full command is like this:
tar xvf $tarfile --no-anchored \
--transform='s:^\(\./\)\?[^o][^p][^t]/:.deleteme/&:' \
--show-transformed-names \
opt/
The sed-like transform breaks down like
^ only at the path start
\(\./\)\? match ./ if present
[^o][^p][^t] followed by any three characters that do not make "opt"
/ followed by /
The &
in the substitution part stands for the original path, to avoid any possible clashes. The v
and --show-transformed-names
are only for logging the extracted names.
POC. Note the directory sys/opt/
that is spurious and unwanted. The cwd was cleaned before each extraction.
A tar packaged with the ./
prefix:
kkm@buba:~/.tmp/tarext$ tar tvf ../tar.withdot.tar
drwxr-xr-x kkm/kkm 0 2020-01-07 21:26 ./
drwxr-xr-x kkm/kkm 0 2020-01-07 21:26 ./dev/
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 ./dev/file1.dev
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 ./dev/file2.dev
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 ./dev/file3.dev
drwxr-xr-x kkm/kkm 0 2020-01-07 21:29 ./opt/
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 ./opt/file1.opt
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 ./opt/file2.opt
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 ./opt/file3.opt
drwxr-xr-x kkm/kkm 0 2020-01-07 21:26 ./sys/
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 ./sys/file1.sys
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 ./sys/file2.sys
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 ./sys/file3.sys
drwxr-xr-x kkm/kkm 0 2020-01-07 21:26 ./sys/opt/
-rw-r--r-- kkm/kkm 100 2020-01-07 20:45 ./sys/opt/file1.opt
-rw-r--r-- kkm/kkm 100 2020-01-07 20:45 ./sys/opt/file2.opt
-rw-r--r-- kkm/kkm 100 2020-01-07 20:45 ./sys/opt/file3.opt
kkm@buba:~/.tmp/tarext$ tar xvf ../tar.withdot.tar --no-anchored --transform='s:^\(\./\)\?[^o][^p][^t]/:.deleteme/&:' --show-transformed-names opt/
./opt/
./opt/file1.opt
./opt/file2.opt
./opt/file3.opt
.deleteme/sys/opt/
.deleteme/sys/opt/file1.opt
.deleteme/sys/opt/file2.opt
.deleteme/sys/opt/file3.opt
kkm@buba:~/.tmp/tarext$ rm -rf .deleteme/ ; ls -RA
.:
opt
./opt:
file1.opt file2.opt file3.opt
A tar packaged without a prefix:
kkm@buba:~/.tmp/tarext$ tar tvf ../tar.nodot.tar
drwxr-xr-x kkm/kkm 0 2020-01-07 21:26 dev/
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 dev/file1.dev
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 dev/file2.dev
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 dev/file3.dev
drwxr-xr-x kkm/kkm 0 2020-01-07 21:29 opt/
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 opt/file1.opt
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 opt/file2.opt
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 opt/file3.opt
drwxr-xr-x kkm/kkm 0 2020-01-07 21:26 sys/
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 sys/file1.sys
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 sys/file2.sys
-rw-r--r-- kkm/kkm 100 2020-01-07 19:09 sys/file3.sys
drwxr-xr-x kkm/kkm 0 2020-01-07 21:26 sys/opt/
-rw-r--r-- kkm/kkm 100 2020-01-07 20:45 sys/opt/file1.opt
-rw-r--r-- kkm/kkm 100 2020-01-07 20:45 sys/opt/file2.opt
-rw-r--r-- kkm/kkm 100 2020-01-07 20:45 sys/opt/file3.opt
kkm@buba:~/.tmp/tarext$ tar xvf ../tar.nodot.tar --no-anchored --transform='s:^\(\./\)\?[^o][^p][^t]/:.deleteme/&:' --show-transformed-names opt/
opt/
opt/file1.opt
opt/file2.opt
opt/file3.opt
.deleteme/sys/opt/
.deleteme/sys/opt/file1.opt
.deleteme/sys/opt/file2.opt
.deleteme/sys/opt/file3.opt
kkm@buba:~/.tmp/tarext$ rm -rf .deleteme/ ; ls -RA
.:
opt
./opt:
file1.opt file2.opt file3.opt