为什么bash忽略了ls输出中的引号?

时间:2019-08-10 03:30:32

标签: bash

下面是一个脚本及其输出,描述了我今天发现的问题。即使引用了sp.data()输出,ls仍在空白处中断。我改用了bash,只是想知道为什么for file in *.txt会这样。

bash

2 个答案:

答案 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
  • 告诉ls输出换行符分隔列表-1
  • 逐行阅读列表
  • 重新评估变量以使用evil删除引号。我的意思是eval
  • 我在循环内使用ls -l "$file"来检查"$file"是否是有效的文件名。

由于ls,这仍然不适用于所有文件名。我的lstouch "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 -zxargs -0cut -zsort -z)都支持处理以零结尾的字符串/流,仅用于处理所有奇怪的字符串。您可以使用的文件名。

答案 1 :(得分:0)

您可以尝试以下代码段:

#!/bin/bash
while read -r file; do
    echo "$file"
done < <(ls --quote-name *.txt)