在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