下面是一个脚本及其输出,描述了我今天发现的问题。即使引用了sp.data()
输出,ls
仍在空白处中断。我改用了bash
,只是想知道为什么for file in *.txt
会这样。
bash
答案 0 :(得分:1)
为什么bash忽略了ls输出中的引号?
因为分词是在变量扩展的结果上发生的。
在评估一条语句时,shell会经历称为shell expansions的不同阶段。这些阶段之一是"word splitting"。从bash手册中引用,字面拆分确实会将变量拆分成单独的单词:
shell扫描参数扩展,命令替换和算术扩展的结果,这些结果在双引号中没有出现,以进行词拆分。
shell将$ IFS的每个字符视为定界符,并使用这些字符作为字段终止符将其他扩展的结果拆分为单词。 。如果未设置IFS,或者其值恰好是默认值
<space><tab><newline>
,则在结果的开头和结尾处分别是<space>
,<tab>
和<newline>
的序列先前的扩展将被忽略,并且任何不在开头或结尾的IFS字符序列都用于分隔单词。 ...
当shell的$FILES
不在双引号内时,它首先进行“参数扩展”。它将$FILES
扩展为字符串"b.txt" "File with space in name.txt"
。然后出现单词拆分。因此,使用默认的IFS
,结果字符串将在空格,制表符或换行符处进行拆分/分隔。
为防止单词分裂,$FILES
本身必须用双引号引起来,$FILES
的值不可以。
好吧,您可以这样做(不安全):
ls -1 --quote-name *.txt |
while IFS= read -r file; do
eval file="$file"
ls -l "$file"
done
-1
evil
删除引号。我的意思是eval
ls -l "$file"
来检查"$file"
是否是有效的文件名。由于ls
,这仍然不适用于所有文件名。我的ls
和touch "c.txt"$'\x01'
一样会忽略字符不可读的文件名。带有嵌入式换行符的文件名将出现诸如ls $'\n'"c.txt"
之类的问题。
这就是为什么建议在脚本中忘记ls
的原因-ls
仅用于在终端上漂亮打印。在脚本中使用find
。
如果文件名中没有嵌入换行符,则可以:
find . -mindepth 1 -maxdepth 1 -name '*.txt' |
while IFS= read -r file; do
ls -l "$file"
done
如果文件名只是任何文件名,请使用以空值结尾的流:
find . -mindepth 1 -maxdepth 1 -name '*.txt' -print0 |
while IFS= read -r -d'' file; do
ls -l "$file"
done
许多Unix实用程序(grep -z
,xargs -0
,cut -z
,sort -z
)都支持处理以零结尾的字符串/流,仅用于处理所有奇怪的字符串。您可以使用的文件名。
答案 1 :(得分:0)
您可以尝试以下代码段:
#!/bin/bash
while read -r file; do
echo "$file"
done < <(ls --quote-name *.txt)