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

时间:2009-10-30 04:49:55

标签: bash parsing text-files cat

我正在尝试遍历文本文件目录并将它们合并到一个文档中。这很好用,但文本文件包含代码片段,我的所有格式都被折叠到左侧。一条线上的所有前导空格都被剥离。

#!/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

我当然是一个bash noob,但经过高低搜索后我找不到合适的解决方案。显然,BASH一般都讨厌领先的白色空间。

5 个答案:

答案 0 :(得分:40)

正如其他人所指出的那样,使用cat或awk而不是read-echo循环是一种更好的方法 - 避免空白修剪问题(还有其他一些你没有偶然发现),运行得更快,至少用cat,只是更简洁的代码。尽管如此,我还是想让读取回路循环正常工作。

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

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

...周围的脚本还有一些其他问题:尝试将FILES定义为可用的.textile文件列表的行在其周围有引号,这意味着它永远不会扩展为实际的文件列表。执行此操作的最佳方法是使用数组:

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

答案 1 :(得分:4)

这是一种组合文件的过于昂贵的方式。

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

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

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

OR

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

答案 2 :(得分:3)

而不是:

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

这样做:

cat $f >>$OUTPUT

(如果有理由你需要逐行处理,那么在问题中加入它会很好。)

答案 3 :(得分:1)

这使您可以像在原始脚本中一样在每个输入文件之间散布换行符:

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

请注意$FILES没有引用它(因为额外的换行只在所有输出的末尾出现一次),但必须引用$f来保护文件名中的空格,如果它们存在。

答案 4 :(得分:0)

正确答案imo是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