如何仅对存在的目录列表进行 tar

Inb*_*ose 7 ls shell tar shell-script

我有一个要存档的目录列表。但有时它们并不都存在。

我希望能够通过提供目录列表来创建存档,它只会存档现有的,而忽略丢失的。(但如果不存在目录,仍然应该失败)

这对我很有用,因为我正在运行一些持续集成,并且一些流程创建了我想在存档中保留以备将来使用的某些工件,我知道可以创建的所有可能路径,但并不总是确定哪些路径被创建。

假设可能的路径是: here_is_a_dir here_is_another_one yet_another_dir

我通常使用以下命令创建存档:

tar -czf archive.tgz here_is_a_dir here_is_another_one yet_another_dir

如果缺少任何目录,这当然会失败。

理想情况下,它应该是一个简单的命令,不需要脚本来完成。(具体来说,仅在我的环境sh中可用,所以我不能使用任何像bash或其他人一样的花哨的外壳,但这特定于我的环境,将来可能会发生变化,我认为使用其他外壳的答案也可能很好。 )

cas*_*cas 8

使用 的输出ls通常是不明智和不安全的 - 请记住,空格和换行符以及其他 shell 元字符都是文件或目录名称中的有效字符。在许多情况下可以解决此问题,但这样做通常比仅使用正确的工具(即find)更费力。

所以,find改用。例如:

find . -maxdepth 1 -type d \( -name here_is_a_dir -o -name here_is_another_one \
  -o -name yet_another_dir \) -exec tar cfz archive.tgz {} +
Run Code Online (Sandbox Code Playgroud)

这会-type d在当前目录 ( .) 中找到任何匹配的目录 ( ) 并将它们用作tar命令的参数。的\(\)是一个表达式,其中每个所述子表达式的是OR-ED一起使用-o(默认,查找的谓词是AND-ED)。即它读作“maxdepth 1 AND type directory AND (dir1 OR dir2 OR dir3)”。

请注意,如果没有括号强制优先,它将被解释为“maxdepth 1 AND type directory AND dir1 OR (dir2 OR dir3)”,这不会返回所有现有目录的完整列表。大多数情况下,它要么什么都不返回,要么只返回 dir3,这取决于 dir1 是否存在。

如果您希望它在当前目录下的任何位置找到子目录,请删除该-maxdepth 1参数。

如果您希望目录匹配不区分大小写,请使用-iname代替-name。请注意,-nameor的参数-iname可以是模式而不是固定字符串 - 如果所需的目录名称非常相似,这很有用。例如

find . -maxdepth 1 -type d -iname 'dir[123]' -exec tar cfz archive.tgz {} +
Run Code Online (Sandbox Code Playgroud)

-exec ....工作方式很像管道到xargs,但内置于find. 事实上,xargs如果您愿意,您可以通过将所有内容替换-exec-print0 | xargs -0 -r tar cfz archive.tgz. 例如

find . -maxdepth 1 -type d -iname 'dir[123]' -print0 | \
  xargs -0 -r tar cfz archive.tgz 
Run Code Online (Sandbox Code Playgroud)

(这使用 NUL 字符作为输出分隔符,因此与包含空格等的目录名一起使用是安全的-exec-r如果没有输入,该选项告诉 xargs 什么都不做)

  • @DavidConrad 确实如此。这些天,我假设一个 GNU 环境,除非另有明确说明(它**是**最常见的 *nix-like userland,因此是合理的默认值)。并非所有 `find` 版本都支持 `-print0`,但是你必须回溯到很远的历史(paleo-unix :-)才能找到一个不支持的版本。顺便说一句,我注意到这里的其他评论中提到了 `busybox` - `man busybox` 表明它支持 `xargs -r`。就像 freebsd 的 `xargs` 版本一样(一个空选项。实际上并不需要,它默认这样做。只是为了与 GNU 兼容)。 (2认同)

Sté*_*las 7

zsh

dirs_to_archive=(some/dir /some/other/dir and/more/dirs)
existing_dirs=($^dirs_to_archive(/N))
if (($#existing_dirs)); then
  tar -cf - -- $existing_dirs | xz > file.tar.xz
else
  echo >&2 Error: none of the dirs were found
fi
Run Code Online (Sandbox Code Playgroud)

POSIX 等价物(但请注意,POSIX 命令既不是tar也不xz是 POSIX 命令)类似于:

# The list of dirs in "$@" (the only array in POSIX sh language)
set -- some/dir /some/other/dir and/more/dirs

for dir do
  # remove from the array the elements that are not directories like with
  # zsh's / glob qualifier above
  [ -d "$dir" ] && [ ! -L "$dir" ] && set -- "$@" "$dir"
  shift
done
if [ "$#" -gt 0 ]; then
  tar -cf - -- "$@" | xz > file.tar.xz
else
  echo >&2 Error: none of the dirs were found
fi
Run Code Online (Sandbox Code Playgroud)


Inb*_*ose 1

ls使用创建现有目录列表,然后通过管道传递xargs到命令,可以相对轻松地完成此操作tar

解决方案:

ls -d here_is_a_dir here_is_another_one yet_another_dir 2> /dev/null | xargs tar -czf archive.tgz

分解:

  • ls -d只列出目录
  • here_is_a_dir here_is_another_one yet_another_dir要检查的目录列表
  • 2> /dev/null将 stderr 通过管道传输到 /dev/null 所以我们只得到 stdout 输出(没有丢失目录)
  • | xargs将上一个命令中的现有目录列表转换为参数
  • tar -czf archive.tgz使用参数进行存档