7

我正在尝试遍历文本文件目录并将它们组合成一个文档。这很好用,但是文本文件包含代码片段,我的所有格式都被折叠到左边。一行上的所有前导空格都被删除。

#!/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 菜鸟,但在高低搜索之后,我找不到合适的解决方案。显然 BASH 通常讨厌领先的空白。

4

5 回答 5

40

正如其他人指出的那样,使用 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 定义为可用 .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
于 2009-10-30T22:45:10.713 回答
4

代替:

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

做这个:

cat $f >>$OUTPUT

(如果有理由需要逐行做事,最好将其包含在问题中。)

于 2009-10-30T04:54:37.343 回答
4

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

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
于 2009-10-30T05:28:20.723 回答
1

这允许您在每个输入文件之间穿插换行符,就像您在原始脚本中所做的那样:

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

请注意,它$FILES没有被引用以使其工作(否则额外的换行符仅在所有输出的末尾出现一次),但$f必须引用以保护文件名中的空格(如果存在)。

于 2009-10-30T06:16:39.067 回答
0

正确的答案,imo,是这个,转载如下:

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
于 2015-12-09T19:42:12.833 回答