how to suppress directory names in tar archived files? - linux

Suppose I have the following directories and files
inc:
inc/foo.h
inc/bar.h
inc/more.h
src:
src/foo.cc
src/bar.cc
src/more.cc
and I want to generate a tape archive using tar which contains some of those files (all with foo or bar in their name), but as if they are all in one single directory. Thus, after unpacking via tar -xf archive.tar my archive somewhere, I have
somewhere/foo.h
somewhere/bar.h
somewhere/foo.cc
somewhere/bar.cc
Can I do that just with tar and without moving/copying my files? How?

Using GNU tar and tar --help:
File name transformations:
--strip-components=NUMBER
strip NUMBER leading components from file names on extraction
--transform=EXPRESSION, --xform=EXPRESSION
use sed replace EXPRESSION to transform file names
On the face of it, therefore, you use it at extract time:
tar -xf filename.tar --strip-components=1 -C somewhere
The -C somewhere changes directory to somewhere before extracting files.
Since you know about the --strip-components option but it doesn't meet your requirements, the next option to try is --transform. Since you want to remove all leading components of the path, then the expression can be quite simple:
tar -cf filename.tar --transform='s%.*/%%' .
This seems to work; I tested it using:
$ mkdir junk
$ cd junk
$ mkdir inc src
$ for file in inc/foo.h inc/bar.h inc/more.h src/foo.cc src/bar.cc more.cc; do cp ../makefile $file; done
$ tar -cf - --transform='s%.*/%%' . | tar -tvf -
drwxr-x--- jleffler/eng 0 2014-05-06 10:00 ./
-rw-r----- jleffler/eng 1183 2014-05-06 10:00 more.cc
drwxr-x--- jleffler/eng 0 2014-05-06 10:00 inc/
-rw-r----- jleffler/eng 1183 2014-05-06 10:00 more.h
-rw-r----- jleffler/eng 1183 2014-05-06 10:00 bar.h
-rw-r----- jleffler/eng 1183 2014-05-06 10:00 foo.h
drwxr-x--- jleffler/eng 0 2014-05-06 10:00 src/
-rw-r----- jleffler/eng 1183 2014-05-06 10:00 bar.cc
-rw-r----- jleffler/eng 1183 2014-05-06 10:00 foo.cc
$

Related

Print the first line of each file inside a tar.gz without extracting

I'm looking for a command in order to print the first line of every file contained in a tar.gz archive, without extracting it.
Example:
tar -ztvf MyArchive.tar.gz
-rw-r--r-- root/root 3732541752 2020-04-04 03:24 FILE1.TXT
-rw-r--r-- root/root 90493394 2020-04-04 03:16 FILE2.TXT
-rw-r--r-- root/root 103294570 2020-04-03 21:06 FILE3.TXT
-rw-r--r-- root/root 16865694 2020-04-03 21:07 FILE4.TXT
-rw-r--r-- root/root 13176227988 2020-04-03 23:36 FILE5.TXT
I need to print the first line of each FILE*.TXT inside the tar.gz
How can I achieve this?
You could achieve using tar and for loop commands.
for i in $(tar -ztvf MyArchive.tar.gz|grep -i file|awk '{print $NF}')
do
tar xfO MyArchive.tar.gz $i|head -1
done
Using "tar xfO MyArchive.tar.gz filename" to read the content of files inside tar.gz
Try this:
tar zxf MyArchive.tar.gz --to-command="head -n 1"
This command takes files in the tar individually and feeds them into the command "head -n 1".

How to extract correct owner permission by tar?

I want to pack specific some sub-directories contents, and extract them with original permission. In the following sample, it extract the directory a with wrong permission, I prefer it keep the same as original foo. How to do it? What's parameter should I add to tar when archive or extract?
create test sample files :
foo ~/tmp $ mkdir -p a/b ; touch a/f1 a/b/f2
create archive file :
foo ~/tmp $ tar cf a.tar a/b
show tar file content :
foo ~/tmp $ tar tvf a.tar
drwxr-xr-x foo/op 0 2018-05-11 10:33 a/b/
-rw-r--r-- foo/op 0 2018-05-11 10:33 a/b/f2
extract with root user :
foo ~/tmp $ mkdir c
foo ~/tmp $ sudo tar -C c -xpf a.tar
foo ~/tmp $ find c -ls
4743788 4 drwxr-xr-x 3 foo op 4096 May 11 10:34 c
4743789 4 drwxr-xr-x 3 root root 4096 May 11 10:34 c/a
4743790 4 drwxr-xr-x 2 foo op 4096 May 11 10:33 c/a/b
4727961 0 -rw-r--r-- 1 foo op 0 May 11 10:33 c/a/b/f2
What I expect is
4743789 4 drwxr-xr-x 3 foo op 4096 May 11 10:34 c/a
I think it is a correct behavior. Your a.tar does not contain directory c/a so it has to be created during the unpacking. That is done under context of the user you are unpacking the archive with - root.
The tar option -p does preserve the rights to directories but only the existing in the archive.
Note: I'm unpacking it with user smalltalk.
Option when you have already created archive and want to remove files from already created archive
I think you could pack all with:
tar cf a.tar a
which would then produce
tar tvf a.tar
drwxrwxr-x smalltalk/smalltalk 0 2018-05-11 10:01 a/
drwxrwxr-x smalltalk/smalltalk 0 2018-05-11 10:01 a/b/
-rw-rw-r-- smalltalk/smalltalk 0 2018-05-11 10:01 a/b/f2
-rw-rw-r-- smalltalk/smalltalk 0 2018-05-11 10:01 a/f1
Then you can simply delete the directories/files you don't want in this case a/f1:
tar -vf a.tar --delete a/f1
You will get:
tar tvf a.tar
drwxrwxr-x smalltalk/smalltalk 0 2018-05-11 10:01 a/
drwxrwxr-x smalltalk/smalltalk 0 2018-05-11 10:01 a/b/
-rw-rw-r-- smalltalk/smalltalk 0 2018-05-11 10:01 a/b/f2
Since now the directory a/ is part of the packed file you will get correct result when unpacking:
find c -ls
149236 0 drwxrwxr-x 3 smalltalk smalltalk 14 May 11 10:14 c
17208669 0 drwxrwxr-x 3 smalltalk smalltalk 14 May 11 10:01 c/a
36340903 0 drwxrwxr-x 2 smalltalk smalltalk 15 May 11 10:01 c/a/b
36333667 0 -rw-rw-r-- 1 smalltalk smalltalk 0 May 11 10:01 c/a/b/f2
First Edit - Second option -- exclude before packaging
Is to create a tar file without the directory in the first place with --exclude option:
For example:
tar cf a.tar a --exclude=a/f1
Which would create:
tar tvf a.tar
drwxrwxr-x smalltalk/smalltalk 0 2018-05-11 10:01 a/
drwxrwxr-x smalltalk/smalltalk 0 2018-05-11 10:01 a/b/
-rw-rw-r-- smalltalk/smalltalk 0 2018-05-11 10:01 a/b/f2
Then the extracted directory would have correct rights.
Second Edit - adding top most directory
based on comment (many files) - adding directory after the archive has been already created
If you have many files I think the best solution is to add the directory to your already created archive.
You would then create the file as you did in your question:
tar cf a.tar a/b
Then you would add the top directory (--no-recursion option excludes the sub-directories):
tar -rvf a.tar --no-recursion a/
You will then get file with the added directory:
tar tvf a.tar
drwxrwxr-x smalltalk/smalltalk 0 2018-05-11 10:01 a/b/
-rw-rw-r-- smalltalk/smalltalk 0 2018-05-11 10:01 a/b/f2
drwxrwxr-x smalltalk/smalltalk 0 2018-05-11 10:01 a/
Then you will get correct extraction:
find c -ls
68333351 0 drwxrwxr-x 3 smalltalk smalltalk 14 May 11 11:01 c
87520961 0 drwxrwxr-x 3 smalltalk smalltalk 14 May 11 10:01 c/a
149236 0 drwxrwxr-x 2 smalltalk smalltalk 15 May 11 10:01 c/a/b
824154 0 -rw-rw-r-- 1 smalltalk smalltalk 0 May 11 10:01 c/a/b/f2
Separate the archive into two steps, the first step archive as previous
tar cf a.tar a/b
The second step to append the directory without recursion
tar -rf a.tar --no-recursion a

How to tar a branch of file tree?

I currently have some files and directories at this path:
/var/tmp/mydir/
I want to tar the whole path, excluding any other content in 'var' and 'tmp'.
Example:
$ ls /var
tmp
dir1 *(exclude)*
file1 *(exclude)*
$ ls /var/tmp
mydir
dir2 *(exclude)*
file2 *(exclude)*
$ ls /var/tmp/mydir
tarme1
tarme2
tarme3
In this case, I want to tar the directory tree /var/tmp/mydir and the content of 'mydir'.
Use tar -cf <archive_name>.tar /var/tmp/mydir which will give you what you need.
Use man tar to get more help (should be quite easy to understand).
If you want to modify your path some other way consider using -C switch. From man:
-C, --directory DIR
change to directory DIR
Do
tar -c --recursion --file backup.tar tmp/mydir
and
tar -tvf backup.tar
gives me :
drwxrwxr-x ssam/ssam 0 2016-05-02 12:02 tmp/mydir/
-rw-rw-r-- ssam/ssam 0 2016-05-02 12:02 tmp/mydir/tarme3
-rw-rw-r-- ssam/ssam 0 2016-05-02 12:02 tmp/mydir/tarme1
-rw-rw-r-- ssam/ssam 0 2016-05-02 12:02 tmp/mydir/tarme2
which is what you need. You can extract/restore it using
tar -xf backup.tar -C /var
Remember this will overwrite the files in mydir

tar creates an empty folder, how do i get rid of it?

I have the following line
tar -c -v -z -f "$ARCHIVE_PATH/$3_$fileYear$fileMonth.tar.gz" -C "$ARCHIVE_PATH/tmp" .
where
$ARCHIVE_PATH = /opt/colorado/archive/
$3 = IMPORT
$fileYear = 2014
$fileMonth = 06
so the line creates a .tar.gz file called IMPORT_201406.tar.gz in /opt/colorado/archive/ from the files located in /opt/colorado/archive/tmp/
however when i use tar -ztvf "opt/colorado/archive/IMPORT_201406.tar.gz" i see this
-rwxr-xr-x root/root 27 2014-06-04 14:20 ./afile.txt
drwxr-xr-x root/root 0 2014-06-04 14:08 ./opt/
drwxr-xr-x root/root 0 2014-06-04 14:08 ./opt/colorado/
drwxr-xr-x root/root 0 2014-06-04 14:08 ./opt/colorado/archive/
drwxrwxr-x [USER]/[USER] 0 2014-06-04 14:09 ./opt/colorado/archive/tmp/
-rwxr-xr-x root/root 712 2014-06-04 14:20 ./twofile.txt
-rwxr-xr-x root/root 383 2014-06-04 14:20 ./random.cvs
-rwxr-xr-x root/root 27 2014-06-04 14:20 ./helloworld.sh
-rwxr-xr-x root/root 7938 2014-06-04 14:20 ./helloworld.py
from my understanding, if i didn't have -C the /opt/colorado/archive/tmp/ would have been added to every file so adding -C tells tar to move to that directory first, i can see in the list of files however why is the /opt/colorado/archive/tmp/ folder added and is there a way to remove it?
It's adding the directory to the archive because you asked it to. Specifically, you told tar to archive the directory ., so that's what it does. Computers tend to be literal.
If you don't want the directory archived, you'll have to pass the filenames in the directory to tar. Here's one way to do that:
(cd "$ARCHIVE_PATH/tmp"; ls) |
tar -cvzf "$ARCHIVE_PATH/$3_$fileYear$fileMonth.tar.gz" -C "$ARCHIVE_PATH/tmp" -T-
Alternatively, you can execute tar from the directory with the files:
cd "$ARCHIVE_PATH/tmp"; tar -cvzf "$ARCHIVE_PATH/$3_$fileYear$fileMonth.tar.gz" *
I don't know why you get the directory included with its full path, rather than just ., and why it also includes the parent directories in the archive. The version of tar on my system (tar (GNU tar) 1.26) doesn't seem to do that.
This command will generate the tar file:
tar -cvf new.tar.gz my_dir_to_tar/
where new.tar.gz = tar file name
and my_dir_to_tar/ = directory to make a tar

Compressing a list of files contain spaces in their file-names

I’ve been trying to compress a set of files contain spaces in thier file-names using a bash file. The bash file is:
#!/bin/bash
tar -cPf 'myconfigs.tar' `cat myconfigs.list`
The content of myconfigs.list file is:
/home/anas/.config/chromium/Default/Bookmarks
/home/anas/.config/chromium/Default/Login Data
/home/anas/.config/chromium/Default/Login Data-journal
The problem is that files contain spaces in their file-names don’t be included in the result TAR archive.
I tried '', "", %20... but didn’t work.
Thanks for your help in advance.
Anas,
I think you need to use:
tar -cPf myconfigs.tar -T myconfigs.list
instead of your "cat" . cat should work too if you properly escape the filenames inside, but -T is better.
UPDATED (to address your question in question's comments):
I cannot comment in your question (don't have enough reputation), so I decided to improve my answer instead.
The tilde (~) expansion is a shell thing, tar does not support it. However, to achieve what you want to achieve, you can use the following trick:
use relative paths in your myconfigs.list file (relative to the home directory):
.config/chromium/Default/Bookmarks
.config/chromium/Default/Login Data
.config/chromium/Default/Login Data-journal
run tar in such way that it changes directory to your home on startup:
tar -cPf myconfigs.tar -C ~/ -T myconfigs.list
OK, I went ahead and created a sample session illustrating it:
root#web:~ # useradd -m galaxy
root#web:~ # su - galaxy
galaxy#web:~ $ mkdir -p {1,2}/{3,4}/{5,6,7}
galaxy#web:~ $ find . -xdev -type d -exec touch '{}/file.txt' \;
galaxy#web:~ $ cat << EOF > include.lst
> 1/3
> 1/4/5/file.txt
> 1/4/7
> 2/file.txt
> EOF
galaxy#web:~ $ cd 2/3/6
galaxy#web:~/2/3/6 $ tar cjSpf ~/sample.tar.bz2 -C ~/ -T ~/include.lst
galaxy#web:~/2/3/6 $ cd
galaxy#web:~ $ tar tjvf sample.tar.bz2
drwx------ galaxy/galaxy 0 2014-02-19 04:10 1/3/
-rw------- galaxy/galaxy 0 2014-02-19 04:10 1/3/file.txt
drwx------ galaxy/galaxy 0 2014-02-19 04:10 1/3/7/
-rw------- galaxy/galaxy 0 2014-02-19 04:10 1/3/7/file.txt
drwx------ galaxy/galaxy 0 2014-02-19 04:10 1/3/6/
-rw------- galaxy/galaxy 0 2014-02-19 04:10 1/3/6/file.txt
drwx------ galaxy/galaxy 0 2014-02-19 04:10 1/3/5/
-rw------- galaxy/galaxy 0 2014-02-19 04:10 1/3/5/file.txt
-rw------- galaxy/galaxy 0 2014-02-19 04:10 1/4/5/file.txt
drwx------ galaxy/galaxy 0 2014-02-19 04:10 1/4/7/
-rw------- galaxy/galaxy 0 2014-02-19 04:10 1/4/7/file.txt
-rw------- galaxy/galaxy 0 2014-02-19 04:10 2/file.txt
galaxy#web:~ $
This should give you a start :)
Don't use cat's output like that otherwise space is considered a delimiter and a separete argument to the tar command.
You can use -T tar option:
tar -cPv -T myconfigs.list -f myconfigs.tar

Resources