代码之家  ›  专栏  ›  技术社区  ›  Brandon Durham

为什么在第一次迭代后这个循环会中断?

  •  2
  • Brandon Durham  · 技术社区  · 7 年前

    尝试遍历目录中的所有文件,检查它们是否存在字符串,如果不存在,则添加该字符串。这就是我所拥有的:

    #!/bin/bash
    FILES=*
    for f in $FILES
    do
        echo "Processing $f file..."
        if grep -Fxq '<?xml version="1.0" encoding="UTF-8"?>' $f
        then
            continue
        else
            echo '<?xml version="1.0" encoding="UTF-8"?>' | cat - $f > temp && mv temp $f
        fi
    done
    

    但是脚本在第一个循环后停止。知道为什么吗?

    2 回复  |  直到 7 年前
        1
  •  2
  •   sjsam    7 年前

    更简单的解决方案是使用就地编辑选项 -i sed 工具如下

    sed -i  '1{/^<?xml version="1.0" encoding="UTF-8"?>/!{
    s/^/<?xml version="1.0" encoding="UTF-8"?>\n/}}' /path/to/files/*
    

    我们在上面做什么

    • 就地选项 -一、 具有 sed公司 对写入文件的文件进行任何更改。
    • 通过 1{} 我们正在处理文件的第一行
    • 这个 /^<?xml version="1.0" encoding="UTF-8"?>/! 零件检查字符串是否为 不是 (注意!在末尾)出现在行的开头。
    • 如果上述条件不成立,则替换行的开头( ^ )与 <?xml version="1.0" encoding="UTF-8"?>\n 使用

           s/^/<?xml version="1.0" encoding="UTF-8"?>\n/
      
    • 其余部分按正确顺序闭合花括号:)

    也就是说,在您的原始脚本中,我看到了如下变量 FILES . 不鼓励使用大写变量作为用户变量,因为它们被保留为环境变量,可能会导致冲突。所以使用 files 相反

    再次做

    file=*
    

    具有以下含义: [ word splitting ] 如果您有包含空格甚至新行的非标准文件,则会产生不想要的结果。你能做的是

    files=( * ) # This put the files in an array
    for file in "${files[@]}" # Double quoting the array prevents word splitting
    do
     # Do something with "$file" but why bother when you've a one-liner with sed? ;-)
    done
    

    注: 对于 人工就诊 [ here ]

        2
  •  1
  •   PesaThe    7 年前

    我想澄清一下我在评论中看到的关于分词和文件名扩展的一些事情。

    使用变量赋值时,引用 Bash Reference Manual ,只执行以下扩展:颚化符扩展、参数扩展、命令替换、算术扩展。这意味着变量中实际上只有一个星号 $files 因为没有文件名扩展。因此,在这一点上,您不需要担心换行符、空格等,因为变量中没有实际的文件。你可以用 declare -p files .

    这就是在分配给变量时不必引用的原因。

    var=$othervariable
    

    var="$othervariable"
    

    现在,当你使用你的变量 $个文件 在for循环中 for f in $files (请注意,您不能引用 $个文件 这里是因为文件名扩展不会发生)该变量被扩展,并且 经历分词 . 但实际价值是 只是 星号和分词对结果没有任何影响!引用 manual 再一次:

    分词后,除非已设置-f选项(请参见集合 内置),Bash扫描每个单词中的字符*?,和[。 如果出现其中一个字符,则该单词被视为 模式,并替换为按字母顺序排序的文件名列表 匹配模式(请参见模式匹配)。

    这意味着文件名扩展已经完成 之后 变量扩展和分词。因此,通过文件名扩展扩展扩展的文件不会被IFS拆分!因此,以下代码可以正常工作:

    #!/usr/bin/env bash
    
    files=*
    for f in $files; do
       echo "<<${f}>>"
    done
    

    并正确输出:

    <<file    with many     spaces>>
    <<filewith* weird   characters[abc]>>
    <<normalfile>>
    

    显然,这是一个较短的版本 for f in * $个文件 . 你也一定要引用 $f 在你的循环中,这个扩展确实经历了分词。