【问题标题】:How do I tar a directory of files and folders without including the directory itself?如何在不包括目录本身的情况下对文件和文件夹的目录进行 tar?
【发布时间】:2010-10-30 17:05:16
【问题描述】:

我通常这样做:

tar -czvf my_directory.tar.gz my_directory

如果我只想在 my_directory 中包含所有内容(包括任何隐藏的系统文件),而不是目录本身,该怎么办?我不想要:

my_directory
   --- my_file
   --- my_file
   --- my_file

我想要:

my_file
my_file
my_file

【问题讨论】:

  • 这是tar -czf 的默认行为吗?在我的情况下,它只存储文件而不是目录。当我只是tar 包含它的目录但使用tar -czf 它只是添加文件。

标签: archive tar gzip


【解决方案1】:

命令

创建标准存档文件。

find my_directory/ -maxdepth 1 -printf "%P\n" | tar -cvf my_archive.tar -C my_directory/ -T -

打包的文件和目录位于存档的根目录中,没有路径信息,更深的文件具有相对路径。
文件和目录前面没有奇怪的“./”。 ('./file')
没有特殊文件 '.'包括在内。

似乎需要另一个工具,如 findls (ls -A -1) 来实现这些目标,而 tar 仅使用其参数无法选择文件并创建具有此类要求的存档。

使用上述命令创建一个归档 tar 文件,该文件可以进一步处理或交付给某人,而不会看起来很奇怪,也不需要解释或解包工具。

参数说明

-maxdepth 1
最多下降 1 级 - 无递归。
-printf
在标准输出上打印 format
%P 文件名,其中删除了它所在的起始点的名称。
\n 换行
printf 不在字符串末尾添加换行符。必须在此处添加

焦油:
-C DIR, --directory=DIR
切换到目录 DIR

-T FILE, --files-from=FILE
获取要从 FILE 中提取或创建的名称
-
上面的那个 FILE 是 标准输入,来自管道


对其他解决方案的评论。

使用@aross 描述的解决方案可能会获得相同的结果。
这里与解决方案的区别在于哪个工具正在执行递归。如果您将工作留给find,则每个文件路径名称都会通过管道。它还发送所有目录名称,带有 --no-recursion 的 tar 忽略或添加为空目录,然后是每个目录中的所有文件。如果从find 读取的文件中出现意外输出错误,tar 将不知道也不关心发生了什么。
但是通过进一步检查,例如处理来自 find 的错误流,这可能是一个很好的解决方案,其中需要对文件进行许多选项和过滤器。
我更喜欢将递归保留在 tar 上,它看起来确实更简单,因此更稳定。
以我复杂的目录结构,在tar不会报错的情况下,我更有信心归档完整。

@serendrewpity 提出的使用find 的另一个解决方案似乎很好,但它在带有空格的文件名上失败。不同之处在于 $() 子 shell 提供的 find 的输出是按空格分隔的。或许可以使用 printf 添加引号,但这会使语句更加复杂。

在使用 ../my_archive.tar 作为 tar 路径时,没有理由 cd 进入 my_directory 然后返回,因为 TAR 有 -C DIR--directory=DIR 命令,它们就是为此目的而存在的。

使用.(点)将包括点

使用 * 会让 shell 提供输入文件列表。可以使用 shell 选项来包含点文件。但这很复杂。该命令必须在允许的 shell 中执行。启用和禁用必须在 tar 命令之前和之后完成。如果将来存档的根目录包含太多文件,它将失败。

最后一点也适用于所有不使用管道的解决方案。

大多数解决方案都是创建一个目录,其中包含文件和目录。这几乎是人们所不希望的。

【讨论】:

  • 您可以添加2>/dev/null 来查找。那么你保证只有文件名/路径
  • 请解释一下。我不明白这将如何工作。您的意思是在未经许可的情况下过滤掉错误,例如无法访问的文件吗? 1. 我想查看错误。如果我不希望包含所有文件,我宁愿使用命令排除这些文件。 2.管道仅适用于标准输出。打印到 stderr 的任何内容都不会到达管道右侧的 tar |,但默认情况下会打印到控制台。
  • 过滤掉错误是我的想法,是的,我不确定stderr是否也会被管道传输......但是没有问题,是吗?
  • 除非您的意思是文件存在读取错误...这意味着您的文件没有权限。非常不寻常,但可能。在这种情况下,您可以将 --ignore-failed-read 添加到 tar
【解决方案2】:

TL;DR(没有./ 也没有./file1!)

find /my/dir/ -printf "%P\n" | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

在某些条件下(仅归档文件、目录和符号链接):

find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

说明

不幸的是,以下内容在存档中包含一个父目录./

tar -czf mydir.tgz -C /my/dir .

您可以使用--transform 配置选项将所有文件移出该目录,但这并不能摆脱. 目录本身。驯服命令变得越来越困难。

您可以使用$(find ...) 将文件列表添加到命令中(如magnus' answer),但这可能会导致“文件列表太长”错误。最好的方法是将它与 tar 的 -T 选项结合起来,像这样:

find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

基本上它的作用是列出目录下的所有文件 (-type f)、链接 (-type l) 和子目录 (-type d),使用 -printf "%P\n" 使所有文件名相对,然后将其传递给 tar命令(它使用 -T - 从 STDIN 获取文件名)。需要 -C 选项,以便 tar 知道具有相对名称的文件所在的位置。 --no-recursion 标志是为了使 tar 不会递归到它被告知归档的文件夹(导致重复文件)。

如果你需要对文件名做一些特殊的事情(过滤、跟随符号链接等),find 命令非常强大,你可以通过删除上述命令的tar 部分来测试它:

$ find /my/dir/ -printf "%P\n" -type f -o -type l -o -type d
> textfile.txt
> documentation.pdf
> subfolder2
> subfolder
> subfolder/.gitignore

例如要过滤PDF文件,添加! -name '*.pdf'

$ find /my/dir/ -printf "%P\n" -type f ! -name '*.pdf' -o -type l -o -type d
> textfile.txt
> subfolder2
> subfolder
> subfolder/.gitignore

非 GNU 查找

该命令使用printf(在GNU find 中可用)告诉find 使用相对路径打印其结果。但是,如果您没有 GNU find,这可以使路径相对(使用 sed 删除父级):

find /my/dir/ -type f -o -type l -o -type d | sed s,^/my/dir/,, | tar -czf mydir.tgz --no-recursion -C /my/dir/ -T -

【讨论】:

  • 很好的答案。非常精细,最重要的是完美解决了问题。
  • 不错的解决方法。为什么 tar 如此 stup1d?
  • @SandRock 我同意使用 tar 实现如此基本的东西是如此棘手,这很奇怪。可能只是历史原因。
  • 这样,我在.bashrc中为它定义一个函数,命名为tar_content
【解决方案3】:
tar -czvf mydir.tgz -C my_dir/ `ls -A mydir`

在 mydir 上一层运行它。 这不包括任何 [.] 或其他内容。

【讨论】:

  • 这也不包括任何带空格的文件/目录。 link
【解决方案4】:
cd DIRECTORY
tar -czf NAME.tar.gz  *

星号将包括所有内容,甚至是隐藏的内容

【讨论】:

    【解决方案5】:
    function tar.create() {
            local folder="${1}"
            
            local tar="$(basename "${folder}")".tar.gz
            
            cd "${folder}" && tar -zcvf "../${tar}" .; cd - &> /dev/null
    }
    

    例子:

    tar.create /path/to/folder
    

    不客气。

    【讨论】:

      【解决方案6】:

      这对我有用。

      tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -maxdepth 1 -printf '%P ')
      

      你也可以使用

      tar -cvf my_dir.tar.gz -C /my_dir/ $(find /my_dir/ -mindepth 1 -maxdepth 1 -printf '%P ')
      

      在第一个命令中,find 返回 my_dir 的文件和子目录的列表。 但是,目录 my_dir 本身作为 '.' 包含在该列表中。 -printf 参数会删除包含“.”的完整路径还有所有 但是 printf 的格式字符串 '%P ' 中的 my_dir 的文件和子目录,并且可以通过 find 命令结果中的前导空格看到.

      这对 TAR 来说不是问题,但如果您想解决这个问题,请在第二个命令中添加 -mindepth 1

      【讨论】:

        【解决方案7】:
        # tar all files within and deeper in a given directory
        # with no prefixes ( neither <directory>/ nor ./ )
        # parameters: <source directory> <target archive file>
        function tar_all_in_dir {
            { cd "$1" && find -type f -print0; } \
            | cut --zero-terminated --characters=3- \
            | tar --create --file="$2" --directory="$1" --null --files-from=-
        }
        

        安全地处理带有空格或其他异常字符的文件名。您可以选择在 find 命令中添加 -name '*.sql' 或类似过滤器以限制包含的文件。

        【讨论】:

          【解决方案8】:
          cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd - 
          

          应该在一行中完成这项工作。它也适用于隐藏文件。至少在 bash 中,“*”不会通过路径名扩展来扩展隐藏文件。下面是我的实验:

          $ mkdir my_directory
          $ touch my_directory/file1
          $ touch my_directory/file2
          $ touch my_directory/.hiddenfile1
          $ touch my_directory/.hiddenfile2
          $ cd my_directory/ && tar -zcvf ../my_dir.tgz . && cd ..
          ./
          ./file1
          ./file2
          ./.hiddenfile1
          ./.hiddenfile2
          $ tar ztf my_dir.tgz
          ./
          ./file1
          ./file2
          ./.hiddenfile1
          ./.hiddenfile2
          

          【讨论】:

          • 这也适用于带有空格或其他特殊字符的文件。干得好!
          • 不完美 - tar 文件包含 '.'还有./file1 而不仅仅是file1。我喜欢下面 mateusza 的解决方案,在解压缩时使用 --strip-components。
          • @Ivan 如果您将. 替换为*,那么命令将为cd my_directory/ &amp;&amp; tar -zcvf ../my_dir.tgz * &amp;&amp; cd ..,那么它将按您的预期工作。
          • @jmathew 你也可以使用子shell,这样你当前shell的工作目录就不会改变:$ (cd my_directory/ &amp;&amp; tar -zcvf ../my_dir.tgz .)
          • 有谁知道为什么这么复杂?似乎是对tar 创作者的重大疏忽……
          【解决方案9】:

          我发现的最简单的方法:

          cd my_dir && tar -czvf ../my_dir.tar.gz *

          【讨论】:

          • 不包含隐藏文件。
          【解决方案10】:
          cd my_directory && tar -czvf ../my_directory.tar.gz $(ls -A) && cd ..
          

          这个对我有用,它包含所有隐藏文件,而不会将所有文件放在名为“。”的根目录中。比如tomoe's answer :

          【讨论】:

          【解决方案11】:

          看看--transform/--xform,它让您有机会在文件添加到存档时按摩文件名:

          % mkdir my_directory
          % touch my_directory/file1
          % touch my_directory/file2
          % touch my_directory/.hiddenfile1
          % touch my_directory/.hiddenfile2
          % tar -v -c -f my_dir.tgz --xform='s,my_directory/,,' $(find my_directory -type f)
          my_directory/file2
          my_directory/.hiddenfile1
          my_directory/.hiddenfile2
          my_directory/file1
          % tar -t -f my_dir.tgz 
          file2
          .hiddenfile1
          .hiddenfile2
          file1
          

          变换表达式与sed类似,我们可以使用/以外的分隔符(上例中为,)。
          https://www.gnu.org/software/tar/manual/html_section/tar_52.html

          【讨论】:

          • 我会这样做。其他任何东西都只是一个黑客!
          • 这是一个更好的解决方案。
          • 这是最好的解决方案。
          • 很好的解决方案,但可能会导致file list too longMy solution 可以防止这种情况发生并且更加灵活。
          • 这是一个很好的解决方案。您还可以为多个路径多次传递--xform
          【解决方案12】:

          使用 tar 的-C 开关:

          tar -czvf my_directory.tar.gz -C my_directory .
          

          -C my_directory 告诉 tar 将当前目录更改为my_directory,然后. 表示“添加整个当前目录”(包括隐藏文件和子目录)。

          确保在执行. 之前执行-C my_directory,否则您将获得当前目录中的文件。

          【讨论】:

          • +1 谢谢!那是该死的'。我失踪了。好烦
          • "与大多数选项不同,-C 在它出现在要处理的文件列表中的那一点处进行处理。考虑以下命令:tar --create --file=foo.tar -C /etc passwd hosts -C /lib libc.a"apl.jhu.edu/Misc/Unix-info/tar/tar_65.html 我总是尝试tar -czvf my_directory.tar.gz * -C my_directory 和那是行不通的。 -C 位置很重要!该死的焦油...
          • 不完美 - tar 文件包含 '.'还有./file1 而不仅仅是file1。我喜欢下面 mateusza 的解决方案,在解压缩时使用 --strip-components。
          • @Superole: shell 在运行 tar 之前替换通配符。另请注意,使用* 之类的通配符不会包含隐藏文件(这是最初的要求)。
          • 它创建 .作为 .tar.gz 中的根目录。
          【解决方案13】:

          This Answer 应该适用于大多数情况。但是请注意文件名是如何存储在 tar 文件中的,例如,./file1 而不仅仅是file1。我发现在使用此方法操作 BuildRoot 中用作包文件的 tarball 时会出现问题。

          一种解决方案是使用一些 Bash glob 列出除 .. 之外的所有文件,如下所示:

          tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *
          

          这是我从this answer学到的技巧。

          如果没有匹配 ..?*.[^.]* 的文件,现在 tar 将返回错误,但它仍然可以工作。如果错误是一个问题(您正在检查脚本中是否成功),则此方法有效:

          shopt -s nullglob
          tar -C my_dir -zcvf my_dir.tar.gz .[^.]* ..?* *
          shopt -u nullglob
          

          虽然现在我们正在搞乱 shell 选项,但我们可能会决定让 * 匹配隐藏文件更简洁:

          shopt -s dotglob
          tar -C my_dir -zcvf my_dir.tar.gz *
          shopt -u dotglob
          

          如果您的 shell 在当前目录中使用 *,这可能不起作用,因此,或者,使用:

          shopt -s dotglob
          cd my_dir
          tar -zcvf ../my_dir.tar.gz *
          cd ..
          shopt -u dotglob
          

          【讨论】:

          • 执行此操作时出现奇怪的错误tar: start.sh: Cannot stat: No such file or directory 我当前目录中的所有文件都会发生这种情况!我该如何避免这种情况?
          • @BrainStone 我得到了完全相同的结果。
          • 这不起作用 - 至少在某些 shell 中(例如,bash,版本 5.0.17,Ubuntu 20.04) - 因为 * glob 在 tar 接管并更改目录之前由 shell 评估(-C my_dir)。因此,它尝试将文件归档在执行 tar 命令的当前目录中,而不是更改的目录my_dir。您可能会很幸运,如果当前目录中的文件名恰好与更改目录中的名称匹配,my_dir,但这通常不可靠。 :) ... 很可能,这就是上述错误的原因。
          • @Trevor 我认为这是第四个示例的工作原理(首先 cd 到目录,然后运行 ​​tar 不带 -C 选项)
          【解决方案14】:

          我会提出以下 Bash 函数(第一个参数是目录的路径,第二个参数是生成的存档的基本名称):

          function tar_dir_contents ()
          {
              local DIRPATH="$1"
              local TARARCH="$2.tar.gz"
              local ORGIFS="$IFS"
              IFS=$'\n'
              tar -C "$DIRPATH" -czf "$TARARCH" $( ls -a "$DIRPATH" | grep -v '\(^\.$\)\|\(^\.\.$\)' )
              IFS="$ORGIFS"
          }
          

          可以这样运行:

          $ tar_dir_contents /path/to/some/dir my_archive
          

          它将在当前目录中生成存档my_archive.tar.gz。它适用于隐藏 (.*) 元素以及文件名中带有空格的元素。

          【讨论】:

          • 避免使用ls link
          【解决方案15】:

          如果是 Unix/Linux 系统,并且你关心隐藏文件(会被 * 忽略),你需要这样做:

          cd my_directory
          tar zcvf ../my_directory.tar.gz * .??*
          

          不知道Windows下的隐藏文件是什么样子的。

          【讨论】:

          • 这会丢失带有 1 个字符名称的点文件,例如 .a
          【解决方案16】:
           tar -cvzf  tarlearn.tar.gz --remove-files mytemp/*
          

          如果文件夹是 mytemp,那么如果您应用上述内容,它将压缩并删除文件夹中的所有文件,但不要管它

           tar -cvzf  tarlearn.tar.gz --remove-files --exclude='*12_2008*' --no-recursion mytemp/*
          

          您可以提供排除模式,也可以指定不查看子文件夹

          【讨论】:

            【解决方案17】:

            使用 pax。

            Pax 是一个已弃用的软件包,但它以一种简单的方式完美地完成了这项工作。

            pax -w > mydir.tar mydir
            

            【讨论】:

            • 最实用,能胜任+1
            • 此命令创建 mydir.tar,其内容为:mydir/file1 mydir/file2,正是要避免的。
            【解决方案18】:
            tar -C my_dir -zcvf my_dir.tar.gz `ls my_dir`
            

            【讨论】:

              【解决方案19】:

              您也可以像往常一样创建存档并将其解压缩:

              tar --strip-components 1 -xvf my_directory.tar.gz
              

              【讨论】:

              • 此解决方案特别适用于您使用在所有需求已知之前创建的 tarball 的情况...
              • 请注意--strip-components 是 GNU 扩展。
              • 这个答案可以通过在上下文中提供“像往常一样”的例子来改进。
              • 如果 tar 的创建在我这边,但提取不是在我这边,并且不希望 . 作为根目录,这不会解决问题。
              【解决方案20】:
              cd my_directory
              tar zcvf ../my_directory.tar.gz *
              

              【讨论】:

              • Hal 明确询问了隐藏文件。你还需要 .??*.
              • -1:这不会将隐藏文件添加到 tar。请参阅 tbman 的回答。
              猜你喜欢
              • 2019-05-30
              • 1970-01-01
              • 2010-11-02
              • 1970-01-01
              • 1970-01-01
              • 2011-08-07
              • 2015-08-03
              • 1970-01-01
              相关资源
              最近更新 更多