Contents

在Linux中迭代带有空格的文件列表

1. 概述

在本文中,我们将学习如何遍历名称中包含空格的文件列表。这是 bash 中的常见问题,其中空格通常被解释为分隔符。

我们将看到如何使用find 来克服这个问题。然后我们可以将命令或文件重定向到while循环并使用read 读取每个条目。

最后,我们将学习如何将文件名存储在数组中,并通过在元素名称周围使用引号来遍历它们。

2. 使用find和*-exec*参数

**使用空格迭代文件名的最简单方法之一是使用find。*我们可以使用-exec*参数对每个匹配条件的文件执行命令:

$ find [parameters] -exec [command] {} \;

我们使用*{}指示需要文件路径的位置,并使用;* 终止*-exec*参数。

例如,让我们编写一个 oneliner 来调用md5sum并使用今天修改的所有文件:

$ find -type f -mtime 0 -exec md5sum {} \;

假设我们有一个包含四个文件的目录,其中两个是今天修改的,两个是几天前修改的:

$ ls -la
total 16
drwxr-xr-x 2 blogdemo users 4096 Mar 24 12:57 ./
drwxr-xr-x 5 blogdemo users 4096 Mar 24 12:32 ../
-rw-r--r-- 1 blogdemo users 1036 Mar 24 12:57 with\ spaces
-rw-r--r-- 1 blogdemo users  346 Mar 24 12:53 without_spaces
-rw-r--r-- 1 blogdemo users 2301 Mar 20 15:21 with\ spaces
-rw-r--r-- 1 blogdemo users  410 Mar 20 16:09 without_spaces

现在,让我们执行我们的行:

$ find -type f -mtime 0 -exec md5sum {} \;
a998be9e05a68a1b08eff4a3d222dc30  ./with spaces
3278a3283405a0a1d12275c0ed0a4cef  ./without_spaces

我们可以看到,它使用今天修改的文件执行了md5sum。*此外,名为“with spaces”*的文件也没有任何问题。

3. 将循环转换为find -exec方法

**但是如果我们有一个循环来遍历文件名呢?在这种情况下,我们需要将循环的内容分离到一个新脚本中,并使用find -exec调用它。**另外,我们可能需要自定义find的参数来过滤我们需要的文件。

首先,让我们看看如何将不能使用空格的循环转换为find -exec方法。让我们从一个遍历find结果的for循环开始:

$ for file in $(find files_to_check/ -type f); do
    echo Testing $file:
    case $file in
        *.md5) md5sum -c "$file";;
        *.sha256) sha256sum -c "$file";;
        *) echo Not recognized;;
    esac 
done

假设我们有一个目录  files_to_check/,其中有两个名为“ with spaces.md5 ”和“ without_spaces.sha256 ”的文件。

我们可以看到 for 循环失败,文件名为“ with spaces.md5

Testing files_to_check/with:
Not recognized
Testing spaces.md5:
md5sum: spaces.md5: No such file or directory
Testing files_to_check/without_spaces.sha256:
without_spaces: OK

现在,让我们将循环内容移动到名为check_file.sh的文件中:

$ cat check_file.sh
#!/bin/bash
echo Testing $1:
case $1 in
    *.md5) md5sum -c "$1";;
    *.sha256) sha256sum -c "$1";;
    *) echo Not recognized;;
esac

现在,让我们使用find -exec将每个文件作为第一个参数运行check_file.sh

$ find files_to_check/ -type f -exec ./check_file.sh {} \;
Testing files_to_check/with spaces.md5:
with spaces: OK
Testing files_to_check/without_spaces.sha256:
without_spaces: OK

我们可以看到,它没有问题

4. 将命令的输出重定向到while循环

有时,我们有一个无法轻易分离的循环,而且我们无法使用find获得任何任意文件列表。所以,我们不能总是使用上一节中描述的方法。

**在这种情况下,另一种方法是在while循环中使用read 命令迭代文件名。**我们使用重定向 将文件名提供给while循环。

让我们使用上一节中的相同文件夹进行尝试:

$ find files_to_check/ -type f | while read file; do
    echo Testing $file:
    case $file in
        *.md5) md5sum -c "$file";;
        *.sha256) sha256sum -c "$file";;
        *) echo Not recognized;;
    esac
done

我们得到了正确的输出:

Testing files_to_check/with spaces.md5:
with spaces: OK
Testing files_to_check/without_spaces.sha256:
without_spaces: OK

请注意,当我们read文件时,我们从标准输入一次读取一行并将其存储在变量*$file中。**我们需要确保命令的输出,在本例中为find*,每行只发出一个文件名。**

现在,让我们再次将while循环移动到一个函数中:

$ check_files() {
    while read file; do
        echo Testing $file:
        case $file in
            *.md5) md5sum -c "$file";;
            *.sha256) sha256sum -c "$file";;
            *) echo Not recognized;;
        esac
    done
}

通过这种方式,我们可以更轻松地为它提供不同的输入。例如,我们可以使用ls而不是find,使用参数-1*来获得每行一个路径*:

$ ls -1 files_to_check/*.md5 files_to_check/*.sha256 | check_files
Testing files_to_check/with spaces.md5:
with spaces: OK
Testing files_to_check/without_spaces.sha256:
without_spaces: OK

我们还可以将文件重定向到我们的函数。这非常方便,因为我们可以迭代任何带有空格的字符串列表,而不仅仅是文件名。让我们通过创建一个名为input_files的文件并用一些内容填充它来尝试一下:

$ cat input_files
files_to_check/with spaces.md5
files_to_check/without_spaces.sha256

最后,我们使用该文件作为check_files函数的输入:

$ check_files < input_files
Testing files_to_check/with spaces.md5:
with spaces: OK
Testing files_to_check/without_spaces.sha256:
without_spaces: OK

5. 遍历数组

最后,我们可以构建一个包含文件名的数组。*要遍历该数组,我们使用for循环、*@子索引,并在数组周围加上引号。让我们看看它是如何工作的:

$ {
    LIST_OF_FILES=("file with spaces.txt" "file_without_spaces.txt")
    for file in "${LIST_OF_FILES[@]}"; do
        md5sum "$file";
    done
}
60e52555abf64d22497ea4568e930126  file with spaces.txt
d4f7a054773a41a2310f77f10dbbeb86  file_without_spaces.txt