Find files and tar them (with spaces)

后端 未结 10 1869
北海茫月
北海茫月 2020-11-29 15:35

Alright, so simple problem here. I\'m working on a simple back up code. It works fine except if the files have spaces in them. This is how I\'m finding files and adding th

10条回答
  •  佛祖请我去吃肉
    2020-11-29 16:17

    Big warning on several of the solutions (and your own test) :

    When you do : anything | xargs something

    xargs will try to fit "as many arguments as possible" after "something", but then you may end up with multiple invocations of "something".

    So your attempt: find ... | xargs tar czvf file.tgz may end up overwriting "file.tgz" at each invocation of "tar" by xargs, and you end up with only the last invocation! (the chosen solution uses a GNU -T special parameter to avoid the problem, but not everyone has that GNU tar available)

    You could do instead:

    find . -type f -print0 | xargs -0 tar -rvf backup.tar
    gzip backup.tar
    

    Proof of the problem on cygwin:

    $ mkdir test
    $ cd test
    $ seq 1 10000 | sed -e "s/^/long_filename_/" | xargs touch 
        # create the files
    $ seq 1 10000 | sed -e "s/^/long_filename_/" | xargs tar czvf archive.tgz
        # will invoke tar several time as it can'f fit 10000 long filenames into 1
    $ tar tzvf archive.tgz | wc -l
    60
        # in my own machine, I end up with only the 60 last filenames, 
        # as the last invocation of tar by xargs overwrote the previous one(s)
    
    # proper way to invoke tar: with -r  (which append to an existing tar file, whereas c would overwrite it)
    # caveat: you can't have it compressed (you can't add to a compressed archive)
    $ seq 1 10000 | sed -e "s/^/long_filename_/" | xargs tar rvf archive.tar #-r, and without z
    $ gzip archive.tar
    $ tar tzvf archive.tar.gz | wc -l
    10000 
      # we have all our files, despite xargs making several invocations of the tar command
    
     
    

    Note: that behavior of xargs is a well know diccifulty, and it is also why, when someone wants to do :

    find .... | xargs grep "regex"
    

    they intead have to write it:

    find ..... | xargs grep "regex" /dev/null
    

    That way, even if the last invocation of grep by xargs appends only 1 filename, grep sees at least 2 filenames (as each time it has: /dev/null, where it won't find anything, and the filename(s) appended by xargs after it) and thus will always display the file names when something maches "regex". Otherwise you may end up with the last results showing matches without a filename in front.

提交回复
热议问题