如何避免在 zsh for-loop 中使用空格作为分隔符?

问题描述 投票:0回答:3

我正在尝试制作一个小脚本来转换音乐库中的一些文件。

但是如果我做类似的事情:

#!/usr/bin/zsh                                                                     

for x in $(find -name "*.m4a");
do
    echo $x;
done

在包含以下内容的文件夹中翻译时:

foo\ bar.m4a

它会返回:

foo
bar.m4a

如何防止 for 循环将空格字符解释为分隔符?

我可以用

$(find -name "*.m4a")
替换
$(find -name "*.m4a" | sed "s/ /_/g")
,然后在循环内以另一种方式使用
sed
,但是如果文件名/路径已经包含下划线(或者我可以使用其他字符来代替下划线)怎么办?

有什么想法吗?

for-loop sh zsh separator
3个回答
6
投票

您可以通过双引号来防止命令替换的单词拆分:

#!/usr/bin/zsh                                                                     

for x in "$(find . -name "*.m4a")"
do
    echo "$x"
done

注意命令替换中的双引号与它之外的双引号不冲突(实际上我以前从未注意到这一点)。如果您觉得单引号更具可读性,您可以像在

"$(find -name '*.m4a')"
中一样轻松地使用单引号。无论如何,我通常在
find
初选中使用单引号。

出于同样的原因,在循环体内引用也很重要。它将确保

x
的值作为单个参数传递。

但这绝对是一个混合的、弗兰肯斯坦式的解决方案,循环

find
的输出通常被认为是不安全的。您最好使用 shell 文件名生成 或使用
find
,如下所示:

find . -name '*.mp3' -exec echo {} \;

您可以添加额外的

-exec
primaries,它们将像由
&&
分隔的 shell 命令一样执行。

如果您绝对必须循环

find
输出,请使用空分隔输出而不是换行分隔:

touch 'a b.txt'
touch $'b\nc.txt'
touch $'d\te.txt'

find . -name '*.txt' -print0 \
| while IFS= read -r -d $'\0' fname; do
    printf '%s\n\n' "$fname"
done

4
投票

在这里使用

zsh
的通配设施而不是
find
.

for x in **/*.m4a; do
    echo "$x"
done

$x
的默认设置下,在循环体中引用
zsh
是可选的,但无论如何这样做并不是一个坏主意。


2
投票

我发现了

如这里所建议:https://askubuntu.com/questions/344407/how-to-read-complete-line-in-for-loop-with-spaces

我可以将 IFS(内部字段分隔符)设置为 ' '.

所以这有效:

#!/usr/bin/zsh                                                                                          

IFS=$'\n'
for x in $(find -name "*.m4a");
do
    echo $x;
done

我希望这可以帮助别人!

© www.soinside.com 2019 - 2024. All rights reserved.