如果存在多个目录,则移动目录 - 测试 globbing 模式是否匹配任何内容
If multiple directories exist then move the directories - test if a globbing pattern matches anything
我想知道如何在 shell 脚本中使用 if
语句来检查多个目录是否存在。
例如,如果/tmp
有子目录test1
、test2
、test3
,我想将它们移动到另一个目录。
我正在使用 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
。
此方法使用 ls
和 grep
创建匹配目录列表或在找不到此类目录的情况下写入错误:
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 选项 failglob
和 nullglob
)- 虽然 默认情况下 在这种情况下,它按照 POSIX 的要求行事。 (zsh
,遗憾的是,默认情况下 失败 如果没有匹配项。)
- 在函数内部,检查 1st 参数 (
</code>) 以确定是否找到 <em>any</em> 匹配就足够了:
<ul>
<li>如果第一个参数 <code>
指的是实际存在的文件系统项(如 -e
文件测试运算符的退出代码所示),则意味着该模式确实匹配了一些东西(至少一个,可能更多)。
- 否则,这意味着该模式已按原样传递,这意味着 未找到 个匹配项。
- 请注意,
-e
测试的退出代码 - 由于是函数中的 last 命令 - 隐式用作函数的退出代码作为整个.
我想知道如何在 shell 脚本中使用 if
语句来检查多个目录是否存在。
例如,如果/tmp
有子目录test1
、test2
、test3
,我想将它们移动到另一个目录。
我正在使用 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
。
此方法使用 ls
和 grep
创建匹配目录列表或在找不到此类目录的情况下写入错误:
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 选项failglob
和nullglob
)- 虽然 默认情况下 在这种情况下,它按照 POSIX 的要求行事。 (zsh
,遗憾的是,默认情况下 失败 如果没有匹配项。)
- 警告:
- 在函数内部,检查 1st 参数 (
</code>) 以确定是否找到 <em>any</em> 匹配就足够了: <ul> <li>如果第一个参数 <code>
指的是实际存在的文件系统项(如-e
文件测试运算符的退出代码所示),则意味着该模式确实匹配了一些东西(至少一个,可能更多)。 - 否则,这意味着该模式已按原样传递,这意味着 未找到 个匹配项。
- 请注意,
-e
测试的退出代码 - 由于是函数中的 last 命令 - 隐式用作函数的退出代码作为整个.