【问题标题】:shell - temp IFS as newline only. Why doesn't this work: IFS=$(echo -e '\n')shell - 临时 IFS 仅作为换行符。为什么这不起作用: IFS=$(echo -e '\n')
【发布时间】:2013-10-12 00:39:00
【问题描述】:

我正在尝试在 shell 中使用 for 来迭代带有空格的文件名。我在stackoverflow question 中读到IFS=$'\n' 可以使用,而且似乎工作正常。然后我读到in a comment to one of the answers,为了使它与bash 以外的shell 兼容,可以使用IFS=$(echo -e '\n')

前者有效,后者无效。这条评论被点赞了好几次。我检查了输出,变量似乎不包含换行符。

#!/bin/sh

IFS=$(echo -e '\n')
echo -n "$IFS" | od -t x1
for file in `printf 'one\ntwo two\nthree'`; do
    echo "Found $file"
done

echo

IFS=$'\n'
echo -n "$IFS" | od -t x1
for file in `printf 'one\ntwo two\nthree'`; do
    echo "Found $file"
done


输出显示第一个字段分隔符缺失:

0000000
Found one
two two
three


但是第二个是正确的:

0000000 0a
0000001
Found one
Found two two
Found three


我找到了一个已回答的问题,它可能与我的问题重复也可能不重复:
linux - When setting IFS to split on newlines, why is it necessary to include a backspace? - Stack Overflow

做那里讨论的事情有什么危险吗,IFS=$(echo -en '\n\b')?因为这会在 IFS 中添加一个\b(我检查过)。 \b 可以出现在文件名中吗?我不希望我的代码错误地拆分文件名。

对于如何在for 循环后正确处理 IFS 恢复,您有什么建议吗?我应该将它存储在临时变量中,还是在子shell 中运行 IFS 和 for 语句? (以及如何做到这一点?)谢谢

【问题讨论】:

    标签: bash shell ifs


    【解决方案1】:

    更新 - 将我的伪评论更改为真实答案。

    我认为this answer 应该解释您所看到的行为。特别是命令替换运算符$() 和反引号将从命令输出中去除尾随换行符。但是,第二个示例中的直接分配不会执行任何命令替换,因此可以按预期工作。

    所以我不敢说我​​认为你提到的赞成评论是不正确的。


    我认为恢复 IFS 最安全的方法是将其设置在子 shell 中。您需要做的就是将相关命令放在括号中()

    (
        IFS=$'\n'
        echo -n "$IFS" | od -t x1
        for file in `printf 'one\ntwo two\nthree'`; do
            echo "Found $file"
        done
    )
    

    当然,调用子shell会产生小的延迟,因此如果要重复多次,则需要考虑性能。


    顺便说一句,要非常小心,文件名可以同时包含 \b 和 \n。我认为它们不能包含的唯一字符是斜杠和\0。至少 wikipedia article 上是这么说的。

    $ touch $'12345\b67890'
    $ touch "new
    > line"
    $ ls --show-control-chars
    123467890  new
    line
    $ 
    

    【讨论】:

      【解决方案2】:

      由于换行符被命令替换剥离,正如@DigitalTrauma 正确写的那样,您应该使用不同的、符合 POSIX 的方式。

      IFS='
      '
      

      写一个换行符并将其分配给它。简单有效。

      如果您不喜欢跨越两行的作业,您可以使用printf 作为替代。

      IFS="$(printf '\n')"
      

      【讨论】:

      • 感谢您提供的信息,我会牢记这一点
      • 请删除您的第二个示例,因为它不干净:IFS="$(printf '\n+')" $ echo -n "$IFS" | xxd 00000000: 0a2b + 添加到 IFS。
      • @TomHale 我删除了不需要的 +
      • 你测试了吗?由于here 给出的原因,这不起作用。
      • 我做到了,它有效。顺便说一句,我正在 Ubuntu 上测试 rxvt-unicode(所以我想 /bin/sh 的破折号)
      【解决方案3】:
      IFS=$(printf '\n+'); IFS=${IFS%?}
      

      正如其他人所提到的,命令替换删除了尾随换行符。因此,如果您想要“可读”的内容,请在换行符之后添加一个字符(在本例中为“+”)以防止它被删除,然后使用参数扩展来删除占位符。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-22
        • 1970-01-01
        • 1970-01-01
        • 2021-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-06
        相关资源
        最近更新 更多