1

Say I do this:

tar cvf allfiles.tar $(<mylist.txt)

and mylist.txt contains this:

/tmp/lib1
/tmp/path2/lib2
/path3/lib3

How to tar the files so the tarball contains just

lib1
lib2
lib3

with no directory structure at all?


The -C of --directory options are usually recommended (ref, ref), but there are multiple directories so this doesn't work. Also, --xform looks like it requires a fixed pattern (ref) which we do not have.

4

The --xform argument takes any number of sed substitute expressions, which are very powerful. In your case use a pattern that matches everything until the last / and replace it with nothing:

tar cvf allfiles.tar --xform='s|.*/||' $(<mylist.txt)

Add --show-transformed-names to see the new names.

Note, this substitution applies to all filenames, not just those given on the command line, so, for example, if you have a file /a/b/c and your list just specifies /a, then the final filename is just c, not b/c. You can always be more explicit and provide an exact list of substitutions, eg in your case

--xform='s|^tmp/path2/||;s|^tmp/||;s|^path3/||'

Note, the initial / will be removed by tar (unless you use -P) so the above expressions are missing it. Also, the list of directories has to be sorted so the longest match is done first, else tmp/path2/ won't match as tmp/ has already been removed. But you can automate the creation of this list, eg:

--xform="$(sed <mylist.txt 's|[^/]*$||; s|^/||; s:.*:s|^&||;:' | sort | tr -d '\n')"
  • This does exactly what I need all with one parameter. Thank you. – Drakes Oct 21 '16 at 8:03
2

With GNU tar, you can use -C whenever and as often as you like, with immediate effect.

$ tree
.
├── 1
│   └── aaa
├── 2
│   └── bbb
└── 3
    └── ccc



# Caveat: the directory change is always relative to directory tar is using *at
# that time*, so the following doesn't work:
$ tar -cf foo.tar -C 1 aaa -C 2 bbb -C 3 ccc  
tar: 2: Cannot open: No such file or directory
tar: Error is not recoverable: exiting now

$ tar -cf foo.tar -C 1 aaa -C ../2 bbb -C ../3 ccc

$ tar tf foo.tar
aaa
bbb
ccc


# You can avoid the special case for the first directory by always specifying
# an absolute path:
$ tar -cf foo.tar -C $(pwd)/1 aaa -C $(pwd)/2 bbb -C $(pwd)/3 ccc



# Now let's create that automatically from your file:
$ cat mylist.txt
/tmp/1/aaa
/tmp/2/bbb
/tmp/3/ccc

$ while read -r line; do printf '-C %s %s ' $(dirname "$line") $(basename "$line") ; done < mylist.txt
-C /tmp/1 aaa -C /tmp/2 bbb -C /tmp/3 ccc 

# That looks about right. Let's use it in our tar command:
$ tar -cvf foo.tar $(while read -r line; do printf '-C %s %s ' $(dirname "$line") $(basename "$line") ; done < mylist.txt)
aaa
bbb
ccc
  • This works well. Thank you. I chose the other answer only because with a single parameter it can do what I'm looking for. – Drakes Oct 21 '16 at 8:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.