如果存在多个目录,则移动目录 - 测试 globbing 模式是否匹配任何内容

If multiple directories exist then move the directories - test if a globbing pattern matches anything

我想知道如何在 shell 脚本中使用 if 语句来检查多个目录是否存在。

例如,如果/tmp有子目录test1test2test3,我想将它们移动到另一个目录。

我正在使用 if [ -d /tmp/test* ]; then mv test* /pathOfNewDir 但它不适用于 if 语句部分。

-d 测试只接受一个参数,因此您需要单独测试每个目录。我也不建议移动测试*,因为它可能比您预期的匹配更多。

使用双括号语法测试语法(例如 if [[ -d...),它是 bash 特定的,但往往比单括号语法更清晰且陷阱更少。如果你只需要检查几个目录,你可以用像 if [[ -d /tmp/test1 && -d /tmp/test2 && -d /tmp/test3 ]]; then...

这样的简单语句来完成

您可能想使用 find:

find /tmp -name "test*" -maxdepth 1 -type d -exec mv \{\} /target/directory \;

这会找到直接位于 /tmp 下的所有 test* 目录,无需递归并将它们移动到 /target/directory

此方法使用 lsgrep 创建匹配目录列表或在找不到此类目录的情况下写入错误:

IFS="
" # input is separated with newlines
if dirs=$( ls -1 -F | grep "^test.*/" | tr -d "/" )
then
  # directories found - move them:
  for d in $dirs
  do
    mv "$d" "$target_directory"/
  done
else
  # no directories found - send error
fi

虽然使用 find 完成此类任务似乎可行,但 find 并未根据评论直接提供 OP 要求的匹配数量反馈。

注意: 对任务使用 ls 会对文件名引入一些限制。此方法不适用于包含换行符或通配符的文件名。

不幸的是,shell 的文件测试运算符(例如 -d-f)在 单个文字上运行 仅路径:

  • 条件如 [ -d /tmp/test* ] 将不起作用,因为如果 /tmp/test* 扩展为 多个 匹配项,您将得到 语法错误(只接受了 1 个参数)。
  • bash 变体 [[ -d /tmp/test* ]] 也不起作用,因为在 [[ ... ]].
  • 中没有执行 globbing(路径名扩展)

测试通配模式是否匹配任何东西最干净的方法是定义一个辅助函数(此解决方案符合 POSIX):

exists() { [ -e "" ]; }

使用 [unquoted] 模式调用它,例如:

exists foo* && echo 'HAVE MATCHES'

# or, in an `if` statement:
if exists foo*; then # ...

唯一需要注意的是,如果 shopt -s failglob 在 bash 中生效,如果不匹配,将向 stderr 打印一条错误消息,并且不会执行其余命令。

请参阅下面的功能说明。


应用于您的特定场景,我们得到(使用bash语法):

# Define aux. function
exists() { [[ -e  ]]; }

exists /tmp/test*/ && mv /tmp/test*/ /path/to/new/dir
  • 注意 /tmp/test*/ 中的尾随 / 以确保只有 目录 匹配(如果有的话)。

  • && 确保仅当函数的退出代码指示为真时才执行以下命令。

  • mv /tmp/test*/ ... 一次将所有匹配的目录移动到新的目标目录。


或者,在助手 array 变量中捕获 globbing 结果:

if matches=(/tmp/test*/) && [[ -e ${matches[0]} ]]; then
  mv "${matches[@]}" /path/to/new/dir
fi

或者,进程单独匹配:

for d in /tmp/test*/; do
   [[ -e $d ]] || break # exit, if no actual match
   # Process individual match.
   mv "$d" /path/to/new/dir
done

辅助功能说明 exists() { [ -e "" ]; }:

它利用了几个 shell 功能:

  • 如果您使用 [n 未加引号] 模式调用它,例如 exists foo*,shell 将扩展 foo* 到所有匹配的 files/directories 并传递它们的名称 作为函数的单独参数
  • 如果 没有 匹配项,模式将 原样 传递给函数 - 此行为由 POSIX.
    • 警告:bash 具有允许更改此行为的配置项(shell 选项 failglobnullglob)- 虽然 默认情况下 在这种情况下,它按照 POSIX 的要求行事。 (zsh,遗憾的是,默认情况下 失败 如果没有匹配项。)
  • 在函数内部,检查 1st 参数 (</code>) 以确定是否找到 <em>any</em> 匹配就足够了: <ul> <li>如果第一个参数 <code> 指的是实际存在的文件系统项(如 -e 文件测试运算符的退出代码所示),则意味着该模式确实匹配了一些东西(至少一个,可能更多)。
  • 否则,这意味着该模式已按原样传递,这意味着 未找到 个匹配项。
  • 请注意,-e 测试的退出代码 - 由于是函数中的 last 命令 - 隐式用作函数的退出代码作为整个.