Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I need to search for a file inside a tar.gz file without extracting it. After that, I need to copy the file that was searched (if ever there is) to another folder.

So far I have this, but the copy part of this line gives me an error.

gunzip -c file.tar.gz | tar tvf - | grep filename | -exec cp {} /folder/another_folder \;

Or is there a better way to search for a file inside a tar.gz file without extracting it?

share|improve this question
    
The problem here is that -exec is an option to the find command: it's not a command of its own. Also, how are you planning on copying a file from inside the archive without extracting at least that folder? –  Joseph R. Oct 17 '13 at 1:48
    
so can I change the grep to find instead so that I can use -exec? –  kickass13 Oct 17 '13 at 1:50
    
No. find is for finding files by name in a directory tree not for searching for a pattern within an input text. –  Joseph R. Oct 17 '13 at 1:51
add comment

2 Answers 2

Does your version of tar not support the switch -z?

$ tar ztvf file.tar.gz | grep fliename

This would then return the name of the file if it existed at all in the archive.

Extracting the file

You could do something like this if you wanted to search for the file first, and only if present then extract it.

$ arc="<tarball>"; file="<file to extract>"; \
        tar ztvf $arc | grep $file && tar zxvf $arc $file

Sample Tarball

$ tar ztvf ffmpeg.static.64bit.2013-10-05.tar.gz 
-rwxr-xr-x root/root  19579304 2013-10-05 00:06 ffmpeg
-rwxr-xr-x root/root  19528712 2013-10-05 00:06 ffprobe

Example

$ arc="ffmpeg.static.64bit.2013-10-05.tar.gz"; file="ffmpeg"; \
       tar ztvf $arc | grep $file && tar zxvf $arc $file

-rwxr-xr-x root/root  19579304 2013-10-05 00:06 ffmpeg
ffmpeg

Confirmation

$ ll ffmpeg
-rwxr-xr-x 1 manny manny 19579304 Oct  5 00:06 ffmpeg*

A different directory

If you want to output the extracted file to some other location you can use tar's -C switch.

$ arc="<tarball>"; file="<file to extract>"; \
        tar ztvf $arc | grep $file && tar zxvf $arc -C /path/to/dir $file

Example #1

$ arc="ffmpeg.static.64bit.2013-10-05.tar.gz"; \
       file="ffmpeg"; tar ztvf $arc | grep $file && tar zxvf $arc -C /tmp $file

Confirmation

$ ll /tmp/ffmpeg
-rwxr-xr-x 1 manny manny 19579304 Oct  5 00:06 /tmp/ffmpeg*

Example #2

$ arc=cp210x.tar.gz; file="cp210x/usb-serial/Makefile"; \
       tar ztvf $arc | grep $file && tar zxvf $arc -C /tmp $file

Confirmation

$ ll /tmp/cp210x/usb-serial/Makefile 
-rw-rw-r-- 1 manny manny 388 May 13 01:37 /tmp/cp210x/usb-serial/Makefile

Example #3

Wildcards could also be used if you want to extract a pattern of files.

$ arc=cp210x.tar.gz; file='*Makefile'; \
       tar ztvf $arc | grep -E "$file" && tar zxvf $arc -C /tmp --wildcards "$file"

Confirmation

$ find /tmp/cp210x -ls | grep Makefile
26881948    4 -r--r--r--   1 manny    manny         171 Mar 14  2012 /tmp/cp210x/Linux_3.x.x_VCP_Driver_Source/Makefile
26881960    4 -rw-rw-r--   1 manny    manny         388 May 13 01:37 /tmp/cp210x/usb-serial/Makefile

Details

The above involves a couple of additional changes. We're extracting everything that matches '*Makefile'. Notice that we've wrapped it in single quotes vs. double quotes now. This is to protect the *Makefile from getting accidentally expanded.

grep now includes the switch -E, because we're searching for a regular expression now and not just a single string within the output of tar. Also the argument to grep is also not wrapped in double quotes.

We now use the switch --wildcards to the 2nd tar so that we can extract based on a pattern rather than just a single file. This argument too is now wrapped in double quotes to protect it.

share|improve this answer
    
When supplying a file name to extract, it must be an exact file name (as found in the archive). So if the file you're looking for is called foo and is stored in the archive as bar/foo, you need to call tar xvzf "$arc" bar/foo. That's why the -C option didn't work for me earlier. –  Joseph R. Oct 17 '13 at 2:21
    
@JosephR. - correct. I'll add an example highlighting that too, thanks. –  slm Oct 17 '13 at 2:23
    
@JosephR. - I've added the ability to do a pattern as well. You can use tar's --wildcards switch to do this. –  slm Oct 17 '13 at 2:46
    
+1 Interesting new switch to learn. –  Joseph R. Oct 17 '13 at 9:11
add comment

You can't do this in one step, I'm afraid. You can't (easily) extract part of an archive to a different location from the one it's been archived under. You can probably do something like this:

pattern=filename
file_path="$(tar tzf file.tar.gz 2>/dev/null| grep "$pattern")"
if [ ! -n "$file_path ];then
    tar xvzf file.tar.gz "$file_path"
    cp "$file_path" /new/location
else
    echo "No files matching $pattern found in archive"
fi

Now for that "easily" remark above. The GNU implementation of tar has a --strip-components option that takes a number and strips that many components from the file name before extraction. So you can count how many components there are before the basename so that you can do a sort of "extract here" operation followed by a copy:

pattern=filename
file_path="$(tar tzf file.tar.gz | grep "$pattern" )"
file_name="$(basename "$file_path")"
[ -z "$file_path" ] && echo "No files matching $pattern found." && exit 1
components=0
while [ "$file_path" != '/' ];do
    file_path=$(dirname "$file_path")
    components=$((components+1))
done
tar xvzf file.tar.gz --strip-components "$components" "$file_path" && cp "$file_name" /new/location
share|improve this answer
    
You can use the -C switch to outout your extracted file to a different location. –  slm Oct 17 '13 at 2:12
    
I tried it and it didn't work. Maybe I'm getting the syntax wrong. Can you show me an example, please? –  Joseph R. Oct 17 '13 at 2:13
    
The -C has to come before the name of the file you're extracting. tar zxvf $arc -C /dir $file. –  slm Oct 17 '13 at 2:13
    
I think it does a literal chdir() to that location, so you have to do it before the files are listed, but just a guess on my part. –  slm Oct 17 '13 at 2:15
    
@slm Again, not working. –  Joseph R. Oct 17 '13 at 2:15
show 8 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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