2

Having this:

$ls $des/bin
a.c           analysis.hpp  classify.hpp  main.cpp   split.hpp
a.cpp         a.out         grade.cpp     main.out   student.cpp
analysis.cpp  classify.cpp  grade.hpp     split.cpp  student.hpp
$ for i in $des/bin/*{hpp,cpp}; do echo $i; done | tar czvf files.tar.gz -C $des/bin -T -

/home/user/Desktop/bin/analysis.hpp
/home/user/Desktop/bin/classify.hpp
/home/user/Desktop/bin/grade.hpp
/home/user/Desktop/bin/split.hpp
/home/user/Desktop/bin/student.hpp
/home/user/Desktop/bin/a.cpp
/home/user/Desktop/bin/analysis.cpp
/home/user/Desktop/bin/classify.cpp
/home/user/Desktop/bin/grade.cpp
/home/user/Desktop/bin/main.cpp
/home/user/Desktop/bin/split.cpp
/home/user/Desktop/bin/student.cpp

Now -> even tought, I have changed to dir $des/bin, it still gives absolute path from root, but it should give relative path from the current dir (which is $des/bin after -C), why?

0

2 Answers 2

3

Since $des contains /home/user/Desktop, the list of files you're passing to tar -T - is:

/home/user/Desktop/bin/analysis.hpp
/home/user/Desktop/bin/classify.hpp
/home/user/Desktop/bin/grade.hpp
[...]

You'd need that list to be:

analysis.hpp
classify.hpp
grade.hpp
[...]

If that's the paths you want be stored in the archive.

Typically, you'd do:

(
  cd -P -- "$des/bin" &&
    printf '%s\0' *.[hc]pp |
      tar --null -T - -zcvf -
) > file.tar.gz

(assuming GNU tar or compatible).

If the list of files is small enough, you don't need printf. You can just do:

(
  cd -P -- "$des/bin" &&
    tar -czvf - -- *.[hc]pp
) > file.tar.gz

Or compacted to the shortest form:

(cd -P -- "$des/bin"&&tar czvf - -- *.[hc]pp)>file.tar.gz

If there's a very large number of *.[hc]pp files, that could exceed the maximum size of cmdline+environ that the execve() system call can take. Using printf in a shell where printf is builtin like bash works around that because builtins are not executed by way of the execve() system call, so are not affected by that limitation.

8
  • Just as addtion, is there a one-line solution? So can directly use it in bash without using parenthesis (or without them). Or is there a shorter solution? Commented Jul 29, 2020 at 14:32
  • @milanHrabos, sure you can always put any shell code or script on one line if that takes your fancy (see edit), but why would you want to do that? Commented Jul 29, 2020 at 14:37
  • @StéphaneChazelas I have 2 question. 1) You change to that dir via cd manually, which I did not intend. I wanted to change to that dir via tar -C option, why otherwise would that option be in tar if I could not use it? Why it doesn't work with tar -C option? 2) What is the meaning of cd -P option? According to man page it should take ..dot-dot files as is (literally). But I get no error with cd -P /home/../home but there is no such directory, so how is it meant in man page? Also why do use it your example, when you have no dot-dot in you pathname ($des/bin)?? Commented Jul 29, 2020 at 18:33
  • cd -P -- (or rather CDPATH= cd -P --) is the shell equivalent of the chdir() of other languages (if we ignore the problems with - (or -2, +3)... with some arguments). cd /home/../home should always get you back to /home, but chdir("/home/../home") gets you to /home/../home which may not be /home if for instance /home is a symlink to /foo/bar. Using cd -P makes the shell behave like all other languages, which is why it's recommended to use it in shell scripts so scripts behave similarly when written in shell or another language. Commented Jul 30, 2020 at 9:20
  • (continued...) and also more importantly IMO, so that cd -P -- "$dir" refers to the same directory as any-other-command "$dir" (as that special handling of .. is only done by the cd shell builtin (or pushd for those shells that have a pushd)). Also note that in some shells, the problems with cd without -P are not limited to .. components. Some shells internally replace cd some-dir with cd $PWD/some-dir which behaves differently in the cases when components of $PWD have been renamed since the last time cd was run. Commented Jul 30, 2020 at 9:21
1

GNU tar only

tar --transform "s,.*/\(.*\),$des/bin/\1," -czf ~/files.tar.gz "$des"/bin/*.[ch]pp

is probably what you are looking for (where the first $des/bin/ can be replaced with whatever you like and \1 represent the file names)

tar --transform 's,.*/\(.*\),\1,' -czf ~/files.tar.gz "$des"/bin/*.[ch]pp
5
  • tar --transform results in tar: a: unknown function modifier. Conclusion: you are not talking about tar but about something different...
    – schily
    Commented Jul 30, 2020 at 7:37
  • @schily GNU tar only, thx
    – alecxs
    Commented Jul 30, 2020 at 7:39
  • This is why it is better to use the original name gtar in answers for clarity. Only on Linux, gtar is not available under it's original name.
    – schily
    Commented Jul 30, 2020 at 7:45
  • @schily question is about /home/user/Desktop
    – alecxs
    Commented Jul 30, 2020 at 7:58
  • 1
    Your answer mentions a vendor specific option that has not been marked as such.
    – schily
    Commented Jul 30, 2020 at 8:35

You must log in to answer this question.

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