我正在阅读几本关于 bash 脚本的书,但一直无法理解正确的引号和用法IFS
。也许有人可以帮我举一个带引号的文件名的小例子。从命令行执行此操作,即使文件名中包含空格,也可以正确打印出文件名:
set - *
for i in "$@"; do echo $i; done
这不起作用,因为它在空格处中断:
set - `find . -name "*"`
for i in "$@"; do echo $i; done
也不会:
IFS=$'\0' set - `find . -name "*" -print0`
for i in "$@"; do echo $i; done
IFS=$'\n'
使用和的组合也不会-print
。为什么这些都失败了?
以下操作也会失败,但在这种情况下会出错(“bash:意外标记“do”附近有语法错误”)。为什么?
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
但这有效(注意“;”):
IFS=$'\n'; for i in `find . -name "*" -type f`; do echo $i; done
但此操作会失败,因为文件名根本没有拆分(循环for
仅一次):
IFS=''; for i in `find . -name "*" -type f -print0`; do echo -e "$i\n"; done
那么,为什么第一个和第三个会失败呢?
最后,我是否正确地认为当设置时IFS
,''
与相同$'\0'
?(我在前面的例子中尝试了两者。)如果是这样,为什么我显然需要$'\n'
而不是仅仅\n
?
*Ubuntu Gnome 16.04 中的 Bash 版本为 4.3.42(1)。
答案1
在调试 bash 命令时我学到了两件事:第一)如果它没有像您认为的那样工作,请 echo/printf 输出命令以确保它们按您认为的方式显示。第二)手动运行命令中的命令以查看它们是否正常运行(除非这些命令具有潜在的破坏性,例如:rm、dd、chmod 777 等)。
您究竟想如何完成或完成什么?我的命令似乎可以逐字逐句地正常工作,因此您需要解释您希望它做什么。您的文件名中是否有空格导致命令出现问题?只要您从 IFS 变量中删除空格,它可能会按您想要的方式工作:IFS=$'\n\t'
。该-print0
选项基本上会删除所有行分隔符,因此除非文件名称中有空格,否则您希望从这些命令中获得的所有内容都没有分隔符。对于您的第二组问题:
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
此命令无法正常运行,因为 bash 是这样读取它的:
IFS=$'\n' for i in `find . -name "*" -type f`
do echo $i
done
第一行将 IFS 设置为:$'\n' for i in
。之后,下一行读为:do echo $i
,这不起作用,因为该do
命令需要某种循环作为前一个命令。下一个有效,因为分号告诉 bash 已到达命令末尾,因此:
IFS=$'\n'
for i in `find . -name "*" -type f`
do echo $i
done
最后,我是否正确地认为,当设置 IFS 时,'' 与 $'\0' 相同?(我在前面的例子中尝试了两者。)如果是这样,为什么我显然需要 $'\n' 而不是 \n?
第一部分是的,''
和'\0'
都被视为 NULL,因此它们基本相同。这是因为 bash 将其解释$''
为 ANSI C 字符串。并且您需要在 \n 周围加上引号,以便 bash 将其解释为 C 字符串,这样换行符就会放在 IFS 中。
您可以阅读更多有关这些的内容这里。