我正在尝试遍历文本文件目录并将它们合并到一个文档中。这很好用,但文本文件包含代码片段,我的所有格式都被折叠到左侧。一条线上的所有前导空格都被剥离。
#!/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一般都讨厌领先的白色空间。
答案 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