Bash:移动特定模式的文件

Bash:移动特定模式的文件

我编写了一个脚本,该脚本迭代一组 zip 文件、提取它们并仅将与特定文件名模式匹配的文件移动到另一个文件夹。

剧本:

#!/bin/bash

ARCHIVE_FILEMASK="????-??-??_E_MDT_0_0.7z"
FILEMASK="?????????_?????_A_?_????????????????-?????????"
extractDir=/path/to/dir
dest=/path/to/dest

for f in ${ARCHIVE_FILEMASK}
do
    7z e -aoa -o"${extractDir}" "$f"
    if [ $? != 0 ]
    then
        mv "${extractDir}/${FILEMASK}".xml "$dest"
    fi
done

执行脚本时出现错误:

mv: cannot stat `/path/to/dir/?????????_?????_A_?_????????????????-?????????.xml': No such file or directory

我已经尝试打开 nullglob 和 extglob 但没有帮助。如果我直接从 shell 执行该命令,它运行得很好。其他有类似问题的帖子的答案也没有帮助。

编辑: 在 Paweł Rumian 提到目录名称中可能存在空格问题后,我尝试了 AVJ 的提示并在命令set +x之前添加mv

结果是:

+ mv '/path/to/dir/?????????_??????_A_?_???????????????-?????? ???.xml'/path/to/dest

所以显然我现在必须去掉单引号。知道怎么做吗?

答案1

通常建议在变量扩展周围放置双引号,因为$foo外部引号意味着“获取 的值foo,在空格处拆分它,如果每个结果单词至少匹配一个文件,则将其扩展为通配符模式”,而不是“获取值”的foo”。特别是,当变量foo包含文件名时,"$foo"这就是您所需要的。但这里的变量ARCHIVE_FILEMASKFILEMASK包含通配符模式。因此,您实际上确实希望在使用它们时扩展它们,因此您应该不加引号地使用它们。因此,它们将被解释为以空格分隔的模式列表;这在这里没有什么区别,因为这些变量的值中没有空格。

变量fextractDir包含文件名,因此它们必须保留双引号。

for f in ${ARCHIVE_FILEMASK}
do
    if 7z e -aoa -o"${extractDir}" "$f"; then
        mv "${extractDir}/"${FILEMASK}.xml "$dest"
    fi
done

由于您使用的是 bash,因此如果通配符模式不匹配任何文件,您可以调用shopt -s nullglob将通配符模式扩展为空列表。如果您不向命令传递任何源文件,"${extractDir}/"${FILEMASK}.xmlmv命令将抱怨缺少文件;仅当至少有一个匹配项时,您才可以通过调用它来解决该问题。

shopt -s nullglob
for f in ${ARCHIVE_FILEMASK}
do
    if 7z e -aoa -o"${extractDir}" "$f"; then
        matches=("${extractDir}/"${FILEMASK}.xml)
        if ((${#matches[@]})); then
            mv "${matches[@]}" "$dest"
        fi
    fi
done

1或者更一般地说,如 所示IFS

答案2

解决方案是删除双引号。现在移动命令如下所示:

mv ${extractDir}/${FILEMASK}.xml "$dest"

答案3

这是因为 mv 命令无法找到源文件。源文件名中可能有特殊字符或其他内容。

通过放置执行脚本设置-x在 mv 命令之前并在 mv 命令之后设置 +x。

它将向您显示脚本执行期间正在执行的确切命令。通过直接从 shell 执行该命令进行检查。

答案4

.7z 文件是否包含多个完全相同的文件名实例?我想不会,因为您将它们全部转移到同一个“$dest”目录中。

在这种情况下,您最好将该mv命令放在循环之外,部分原因是您mv总共只运行一个命令(而不是mv每个 .7z 文件运行一个命令),但主要是因为更有可能至少有一个文件与您的图案。并且您可以在变量周围保留双引号,如果文件名中包含 shell 或 glob 重要字符,则这会更安全。

如果单个命令提取的文件太多mv,您可以使用find,如下所示:

find "$extractDir" -name "$FILEMASK.xml" -exec mv {} "$dest/" +

无论如何,最好使用find- 你永远不知道将提取多少文件,这是有用的防御性编程。它还具有如果没有找到匹配文件则不执行任何操作的优点,因此mv不会抱怨丢失文件。

相关内容