让 BASH 脚本 `for` 处理带有空格的文件名(或解决方法)

让 BASH 脚本 `for` 处理带有空格的文件名(或解决方法)

虽然我已经使用 BASH 好几年了,但我对 BASH 脚本的经验相对有限。

我的代码如下。它应该从当前目录中获取整个目录结构并将其复制到$OUTDIR

for DIR in `find . -type d -printf "\"%P\"\040"`
do
  echo mkdir -p \"${OUTPATH}${DIR}\"        # Using echo for debug; working script will simply execute mkdir
  echo Created $DIR
done

问题是,这是我的文件结构的一个示例:

$ ls
Expect The Impossible-Stellar Kart
Five Iron Frenzy - Cheeses...
Five Score and Seven Years Ago-Relient K
Hello-After Edmund
I Will Go-Starfield
Learning to Breathe-Switchfoot
MMHMM-Relient K

注意空格 :-S 并for逐字逐句地接受参数,因此我的脚本的输出如下所示:

Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Learning"
Created Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot"
Created Breathe-Switchfoot

但我需要它从 的输出中抓取整个文件名(一次一行)find。我也尝试过find在每个文件名周围加上双引号。但这没有帮助。

for DIR in `find . -type d -printf "\"%P\"\040"`

并输出以下更改后的行:

Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"""
Created ""
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"Learning"
Created "Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot""
Created Breathe-Switchfoot"

现在,我需要某种可以像这样迭代的方法,因为我还希望对gstreamer以下类似结构中的每个文件运行更复杂的命令。我应该怎么做?

编辑:我需要一个代码结构,允许我为每个目录/文件/循环运行多行代码。如果我说得不清楚,请见谅。

解决方案:我最初尝试过:

find . -type d | while read DIR
do
  mkdir -p "${OUTPATH}${DIR}"
  echo Created $DIR
done

在大多数情况下,这种方法都很好用。但是,我后来发现,由于管道导致 while 循环在子 shell 中运行,因此循环中设置的任何变量后来都不可用,这使得实现错误计数器变得非常困难。我的最终解决方案(来自这个答案):

while read DIR
do
  mkdir -p "${OUTPATH}${DIR}"
  echo Created $DIR
done < <(find . -type d)

这后来允许我在循环内有条件地增加变量,这些变量稍后会在脚本中保持可用。

答案1

您需要将其放入循环findwhile

find ... | while read -r dir
do
    something with "$dir"
done

-printf此外,在这种情况下您不需要使用。

如果愿意,您可以使用空字节分隔符(这是唯一不能出现在 *nix 文件路径中的字符)对名称中带有换行符的文件进行此证明:

find ... -print0 | while read -d '' -r dir
do
    something with "$dir"
done

您还会发现使用$()反引号代替反引号更加灵活和简单。它们可以更轻松地嵌套,并且可以更轻松地引用。这个虚构的例子将说明以下几点:

echo "$(echo "$(echo "hello")")"

尝试使用反引号来做到这一点。

答案2

这个答案几天前我写了一个处理带空格的文件名的脚本示例。

不过,还有一种稍微复杂一些(但更简洁)的方法来实现你想要做的事情:

find . -type d -print0 | xargs -0 -I {} mkdir -p ../theredir/{}

-print0告诉 find 用空字符分隔参数;xargs 的 -0 告诉它期望参数以空字符分隔。这意味着它可以很好地处理空格。

-I {}告诉 xargs 将字符串替换{}为文件名。这也意味着每个命令行只能使用一个文件名(xargs 通常会填充一行中能容纳的文件名)

其余的就显而易见了。

答案3

您遇到的问题是 for 语句将 find 作为单独的参数进行响应。空格分隔符。您需要使用 bash 的 IFS 变量来避免按空格进行拆分。

这里有一个关联解释了如何做到这一点。

IFS 内部变量

解决此问题的一种方法是更改​​ Bash 的内部 IFS(内部字段分隔符)变量,以便它使用默认空格(空格、制表符、换行符)以外的其他字符来分隔字段,在本例中为逗号。

#!/bin/bash
IFS=$';'

for I in `find -type d -printf \"%P\"\;`
do
   echo "== $I =="
done

将 find 设置为在 %P 后输出字段分隔符,并适当设置 IFS。我选择了分号,因为它不太可能出现在文件名中。

另一种方法是直接从 find 中通过-execdo 调用 mkdir,这样您就可以完全跳过 for 循环。如果您不需要进行任何额外的解析的话。

答案4

在您更新的问题中

mkdir -p \"${OUTPATH}${DIR}\"

这应该是

mkdir -p "${OUTPATH}${DIR}"

相关内容