13

I want to tar the files foo and bar into a tar archive archive.tar, but I want them to appear, within the archive, as being within a directory, bazdir. Thus when I untar someplace I want bazdir to be created and foo and bar to be created within it.

How can I do that?

This would be the opposite of:

https://stackoverflow.com/questions/939982/how-do-i-tar-a-directory-of-files-and-folders-without-including-the-directory-it
or
create flat tar archive: ignoring all parents when adding folders

3

4 Answers 4

22

You can use the --transform option. For example:

touch foo bar
tar cf archive.tar foo bar --transform 's,^,bazdir/,'
tar tvf archive.tar 
-rw-r--r-- tigger/tigger     0 2017-10-11 19:32 bazdir/foo
-rw-r--r-- tigger/tigger     0 2017-10-11 19:32 bazdir/bar

For more details and more complex options see How to create a common base folder with tar and how to rename folders? - on the sister site, ask ubuntu.

1
  • 4
    Note that --transform doesn't work with macOS tar. Mar 17, 2021 at 8:27
3

In case you are on macOS, then use option -s instead of --transform.

-s pattern

Modify file or archive member names according to pattern. The pattern has the format /old/new/[ghHprRsS] where old is a basic regular expression, new is the replacement string of the matched part, and the optional trailing letters modify how the replacement is handled. If old is not matched, the pattern is skipped. Within new, ~ is substituted with the match ...

The optional trailing p specifies that after a successful substitution the original path name and the new path name should be printed to standard error.

With above information we can define option -s as show below:

$ tar -c -a -v -s "|.*|prefix/~|p" -f ../out.tar.gz 1.txt 2.txt N.txt

1.txt >> prefix/1.txt
a prefix/1.txt
2.txt >> prefix/2.txt
a prefix/2.txt

where

  • | - is a pattern parts separator instead of default one "/".
  • .* - is a regex to define "old" part of the pattern (matches everything).
  • prefix/~ - is the expression to prepend to "old" part (defined as "~")
  • p - is a pattern option to print original and new name of the file to be archived.

Real world example - archiving all files from git repository including submodules:

$ git ls-files --recurse-submodules | tar -c -a -v --files-from - -s "|.*|1.0.2/~|" --file "dist_1.0.2.tar.gz"

a 1.0.2/.gitignore
a 1.0.2/.gitmodules
a 1.0.2/.prettierignore
a 1.0.2/.prettierrc
a 1.0.2/.vscode/settings.json
a 1.0.2/Gulpfile.mjs
a 1.0.2/Ports.mjs
a 1.0.2/Readme.md
a 1.0.2/Version.cjs
a 1.0.2/app.mjs
a 1.0.2/app.test.mjs
a 1.0.2/jest-fastify-env.mjs
a 1.0.2/jest.babel.config.cjs
a 1.0.2/jest.config.mjs
a 1.0.2/jest.setup.mjs
a 1.0.2/lib/conf/Stack.mjs
a 1.0.2/lib/shared/.gitignore
a 1.0.2/lib/shared/AWSS3Client.cjs
a 1.0.2/lib/shared/Api.cjs
a 1.0.2/lib/shared/Api.test.mjs
a 1.0.2/lib/shared/AppConfig.cjs
a 1.0.2/lib/shared/DBApi.cjs
a 1.0.2/lib/shared/DBClient.cjs
a 1.0.2/lib/shared/DBExporter.mjs
a 1.0.2/lib/shared/DynamoDBClient.cjs
a 1.0.2/lib/shared/DynamoDBConnection.cjs
a 1.0.2/lib/shared/RDS.mjs
a 1.0.2/lib/shared/RDSClient.mjs
a 1.0.2/lib/shared/SecuredApi.cjs
a 1.0.2/lib/shared/SecuredApi.test.mjs
a 1.0.2/lib/shared/SecuredApiErrorIds.cjs
a 1.0.2/lib/shared/SharedEnv.cjs
a 1.0.2/lib/shared/conf/AppEnv.cjs
a 1.0.2/lib/shared/helpers/CognitoTokenDownloader.cjs
a 1.0.2/lib/shared/helpers/CognitoTokenDownloader.test.mjs
a 1.0.2/lib/shared/helpers/HTTPAuthHelper.cjs
a 1.0.2/lib/shared/helpers/HTTPAuthHelper.test.mjs
a 1.0.2/lib/shared/helpers/Preprocessor.cjs
a 1.0.2/lib/shared/helpers/Preprocessor.test.mjs
a 1.0.2/lib/shared/helpers/TokenVerifier.cjs
a 1.0.2/lib/shared/helpers/TokenVerifier.test.mjs
a 1.0.2/lib/shared/helpers/TokenVerifierErrorIds.cjs
a 1.0.2/lib/shared/helpers/TypeChecking.cjs
a 1.0.2/lib/shared/helpers/TypeChecking.test.mjs
a 1.0.2/lib/shared/testability/TokenFixture.cjs
a 1.0.2/lib/shared/types/CustomDateFormatter.cjs
a 1.0.2/lib/shared/types/CustomDateFormatter.test.mjs
a 1.0.2/lib/shared/types/EnvName.cjs
a 1.0.2/lib/shared/types/GenericError.cjs
a 1.0.2/package.json
a 1.0.2/scripts/shared/.gitignore
a 1.0.2/scripts/shared/Prompt.cjs
a 1.0.2/scripts/shared/VersionTool.mjs
a 1.0.2/server.mjs
a 1.0.2/yarn.lock

If you double click on "dist_1.0.2.tar.gz" in Finder.app on macOS, then you will see the folder with name "1.0.2" with all archived files inside it.

3

Assuming that bazdir does not already exist in the current directory, the most straight-forward method that comes to mind is:

$ ln -s . bazdir
$ tar cvf myfile.tar bazdir/{foo,bar}
a bazdir/foo
a bazdir/bar

Once the .tar file is created, we remove the bazdir symlink:

$ rm bazdir

The .tar file now contains the directory prefix bazdir/:

$ tar tvf myfile.tar 
-rw-------  0 jim    wheel       0 Dec 11 13:41 bazdir/foo
-rw-------  0 jim    wheel       0 Dec 11 13:41 bazdir/bar

And when we extract the tar file, bazdir gets created as a true directory:

$ tar xvf myfile.tar 
x bazdir/foo
x bazdir/bar
$ find .
.
./myfile.tar
./foo
./bazdir
./bazdir/bar
./bazdir/foo
./bar
$ file bazdir
bazdir: directory

If you prefer not to touch the source directory (or indeed if it's read-only), then place the symlink elsewhere:

$ ln -s $(pwd) /tmp/bazdir
$ tar cvf /tmp/myfile.tar -C /tmp bazdir/{foo,bar}
a bazdir/foo
a bazdir/bar
$ rm /tmp/bazdir
$ tar tvf /tmp/myfile.tar 
-rw-------  0 jim    wheel       0 Dec 11 13:41 bazdir/foo
-rw-------  0 jim    wheel       0 Dec 11 13:41 bazdir/bar
3
  • Well, +1, but: 1. I needed this 5 years ago and 2. It's a legitimate hack, but I'd rather avoid having to touch the source directory. I suppose you could adapt your approach to use a link from /tmp though.
    – einpoklum
    Dec 11, 2022 at 22:01
  • @einpoklum Re: 1. Bear in mind that one of the purposes of this site is to retain useful answers so that future readers can benefit as well. Re: 2. Good suggestion, I'll include it.
    – Jim L.
    Dec 11, 2022 at 22:03
  • 1
    And IMO, it's not so much a hack as a demonstration of what symlinks are for. Hard links are so that one can refer to a given file by a different name. Symlinks are so that one can refer to a given directory by a different name.
    – Jim L.
    Dec 11, 2022 at 22:10
0

Not very efficient, but clear way is to rsync files to some temporary directory and then tar them.

--transform is a nice feature in many cases, but it gives slightly different archive structure, that may broke unpacking side, when that side is out of our control.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .