In a directory there are files and sub-folders with spaces:
$ find ./test
./test
./test/testdir
./test/testdir/file 1 2 3
./test/testdir 2 3 4
./test/testdir 2 3 4/file 5 6 7
./test/testfile
./test/otherdir
./test/otherdir/otherfile
./test/otherfile
$
This is only test folder example to show the case, but on real environment this is huge folder with hundreds of files and size in total ca. 100GB.
I have a zip file with updates of some files from above folder. Before I unzip update file I would like to do selective backup of each file which will be replaced by this update.
The files in update.zip file are:
$ unzip -Z1 update.zip
testdir/
testdir/file 1 2 3
testdir 2 3 4/
testdir 2 3 4/file 5 6 7
testfile
$
Here is the command which I tried to do such backup which failed:
$ tar czvf backup_before_update.tgz -C ./test/ $(unzip -Z1 update.zip|grep -v \/$|sed -r 's/^/"/;s/$/"/'|paste -sd" ")
tar: "testdir/file: Cannot stat: No such file or directory
tar: 1: Cannot stat: No such file or directory
tar: 2: Cannot stat: No such file or directory
tar: 3": Cannot stat: No such file or directory
tar: "testdir: Cannot stat: No such file or directory
tar: 2: Cannot stat: No such file or directory
tar: 3: Cannot stat: No such file or directory
tar: 4/file: Cannot stat: No such file or directory
tar: 5: Cannot stat: No such file or directory
tar: 6: Cannot stat: No such file or directory
tar: 7": Cannot stat: No such file or directory
tar: "testfile": Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors
$
When I do just "echo" and run the command manually, there is no such error:
$ echo tar czvf backup_before_update.tgz -C ./test/ $(unzip -Z1 update.zip|grep -v \/$|sed -r 's/^/"/;s/$/"/'|paste -sd" ")
tar czvf backup_before_update.tgz -C ./test/ "testdir/file 1 2 3" "testdir 2 3 4/file 5 6 7" "testfile"
$ tar czvf backup_before_update.tgz -C ./test/ "testdir/file 1 2 3" "testdir 2 3 4/file 5 6 7" "testfile"
testdir/file 1 2 3
testdir 2 3 4/file 5 6 7
testfile
$
I can use eval and it works too:
$ eval $(echo tar czvf backup_before_update.tgz -C ./test/ $(unzip -Z1 update.zip|grep -v \/$|sed -r 's/^/"/;s/$/"/'|paste -sd" "))
testdir/file 1 2 3
testdir 2 3 4/file 5 6 7
testfile
$ eval $(echo tar czvf backup_before_update.tgz -C ./test/ $(unzip -Z1 update.zip|grep -v \/$|sed -r 's/^/"/;s/$/"/'))
testdir/file 1 2 3
testdir 2 3 4/file 5 6 7
testfile
$
Why the first command fails even if all given file names are in double quotes?
CodePudding user response:
When you turn on debug with set -x
(and turn off with set -
), you will see that the result of $(...)
is split up in parameters using spaces as delimiters. In your case (no filenames with newline) you was almost done when you created a list of files, each on one line.
GNU tar
can read from a file with the -T
option
-T, --files-from=FILE
Get names to extract or create from FILE.
Unless specified otherwise, the FILE must contain a list of names separated by
ASCII LF (i.e. one name per line). The names read are handled the same way as
command line arguments.
You can use the construction <(cmd)
when you want the result of cmd
being handled as a file.
tar czvf backup_before_update.tgz -C ./test/ -T <(unzip -Z1 update.zip|grep -v \/$)