代码之家  ›  专栏  ›  技术社区  ›  Joel Hooks

读取时保留前导空白>>在bash中逐行写入文件

  •  6
  • Joel Hooks  · 技术社区  · 15 年前

    我试图循环访问一个文本文件目录,并将它们组合成一个文档。这很好,但是文本文件包含代码片段,我的所有格式都将折叠到左侧。一行中所有前导空格都将被删除。

    #!/bin/sh
    OUTPUT="../best_practices.textile"
    FILES="../best-practices/*.textile"
    for f in "$FILES"
    do
      echo "Processing $f file..."
      echo "">$OUTPUT
    
      cat $f | while read line; do 
          echo "$line">>$OUTPUT
      done
      echo >>$OUTPUT
      echo >>$OUTPUT
    done
    

    诚然,我是一个不折不扣的人,但在寻找了高低之后,我找不到合适的解决办法。显然,巴什讨厌主要的空白区。

    5 回复  |  直到 8 年前
        1
  •  3
  •   Willi Mentzel user670265    9 年前

    而不是:

    cat $f | while read line; do 
        echo "$line">>$OUTPUT
    done
    

    这样做:

    cat $f >>$OUTPUT
    

    (如果你有理由一行一行地做事情,最好把它包括在问题中。)

        2
  •  40
  •   Gordon Davisson    15 年前

    正如其他人所指出的,使用cat或awk而不是read echo循环是一种更好的方法——避免空白剪裁问题(以及一些您没有偶然发现的问题),更快地运行,至少使用cat,这只是更干净的代码。尽管如此,我还是想尝试一下让read echo循环正常工作。

    首先,空格修剪问题:read命令自动修剪前导和尾随的空格;通过将ifs变量设置为空来更改其空白定义,可以解决这个问题。另外,read假定行尾的反斜杠意味着下一行是一个连续行,应该与这个连续行拼接在一起;要解决这个问题,请使用它的-r(原始)标志。这里的第三个问题是,许多echo实现解释字符串中的转义序列(例如,它们可能会将\n转换为实际的换行符);要解决此问题,请改用printf。最后,正如一般的脚本卫生规则,当您实际上不需要时,不应该使用cat;而是使用输入重定向。通过这些更改,内部循环如下所示:

    while IFS='' read -r line; do 
      printf "%s\n" "$line">>$OUTPUT
    done <$f
    

    …周围的脚本还有一些其他问题:试图将文件定义为可用文件列表的行。纺织文件周围有引号,这意味着它永远不会扩展为实际的文件列表。最好的方法是使用数组:

    FILES=(../best-practices/*.textile)
    ...
    for f in "${FILES[@]}"
    

    (如果任何文件名中有空格或其他有趣的字符,$F的所有出现都应该用双引号括起来,$output也应该这样做,尽管脚本中已经定义了这一点,因此可以安全地去掉它。)

    最后,有一个 echo "">$OUTPUT 在循环文件的顶部附近,每次通过时都会删除输出文件(即,在末尾,它只包含最后一个.textile文件);这需要移动到循环之前。我不确定这里的目的是在文件的开头放一行空白,还是在文件之间放三行空白(一行在开头,两行在结尾),所以我不确定合适的替换是什么。不管怎样,在解决了所有这些问题之后,我能做的是:

    #!/bin/sh
    OUTPUT="../best_practices.textile"
    FILES=(../best-practices/*.textile)
    
    : >"$OUTPUT"
    for f in "${FILES[@]}"
    do
      echo "Processing $f file..."
      echo >>"$OUTPUT"
    
      while IFS='' read -r line; do 
        printf "%s\n" "$line">>"$OUTPUT"
      done <"$f"
    
      echo >>"$OUTPUT"
      echo >>"$OUTPUT"
    done
    
        3
  •  4
  •   ghostdog74    15 年前

    这是一种非常昂贵的合并文件的方法。

    cat ../best-practices/*.textile >  ../best_practices.textile
    

    如果要在连接时向每个文件添加一个空白(换行符),请使用awk

    awk 'FNR==1{print "">"out.txt"}{print > "out.txt" }' *.textile
    

    awk 'FNR==1{print ""}{print}' file* > out.txt
    
        4
  •  1
  •   Dennis Williamson    15 年前

    这允许您像在原始脚本中那样在每个输入文件之间散布新行:

    for f in $FILES; do echo -ne '\n\n' | cat "$f" -; done > $OUTPUT
    

    注意 $FILES 为使其工作而不加引号(否则,额外的换行在所有输出的末尾只出现一次),但是 $f 必须引用以保护文件名中的空格(如果存在)。

        5
  •  0
  •   Community CDub    7 年前

    我的正确答案是 this ,转载如下:

    while IFS= read line; do
        check=${line:0:1}
    done < file.txt
    

    请注意,它将处理输入从另一个命令(而不仅仅是从实际文件)进行管道传输的情况。

    注意,您还可以简化重定向,如下所示。

    #!/bin/bash
    OUTPUT="../best_practices.textile"
    FILES="../best-practices/*.textile"
    for f in "$FILES"
    do
      echo "Processing $f file..."
      {
      echo
    
      while IFS= read line; do 
          echo "$line"
      done < $f
      echo
      echo;
      } > $OUTPUT
    done