在 bash 中逐行读取>>写入文件时保留前导空格
我正在尝试循环遍历文本文件目录并将它们组合成一个文档。这很好用,但是文本文件包含代码片段,并且我的所有格式都折叠到左侧。一行上的所有前导空格都被删除。
#!/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 总体上讨厌前导空格。
I am trying to loop through a directory of text files and combine them into one document. This works great, but the text files contain code snippets, and all of my formatting is getting collapsed to the left. All leading whitespace on a line is stripped.
#!/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
I am admittedly a bash noob, but after searching high and low I couldn't find a proper solution. Apparently BASH hates the leading white space in general.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
正如其他人指出的那样,使用 cat 或 awk 而不是 read-echo 循环是一种更好的方法——避免空白修剪问题(以及其他一些您没有偶然发现的问题),运行速度更快,至少对于 cat 来说,代码更简洁。尽管如此,我还是想尝试一下让读取回显循环正常工作。
首先,空白修剪问题:read命令自动修剪前导和尾随空白;这可以通过将 IFS 变量设置为空白来更改其空白定义来解决。另外,read 假设行尾的反斜杠意味着下一行是延续,并且应该与这一行拼接在一起;要解决此问题,请使用其 -r(原始)标志。这里的第三个问题是 echo 的许多实现解释字符串中的转义序列(例如,它们可能将 \n 转换为实际的换行符);要解决此问题,请改用 printf。最后,正如一般的脚本卫生规则一样,当您实际上不需要时,不应该使用 cat ;请改用输入重定向。通过这些更改,内部循环如下所示:
...周围的脚本还存在一些其他问题:尝试将 FILES 定义为可用 .textile 文件列表的行有引号,这意味着它永远不会被扩展为实际的文件列表。最好的方法是使用一个数组:(
并且所有出现的 $f 都应该用双引号引起来,以防任何文件名中包含空格或其他有趣的字符 - 也应该使用 $OUTPUT 来做到这一点,尽管因为它是在脚本中定义的,所以实际上可以安全地离开。)
最后,在循环文件顶部附近有一个
echo "">$OUTPUT
,它将删除输出每次都文件(即最后,它只包含最后一个 .textile 文件);这需要移到循环之前。我不确定这里的意图是在文件开头放置一个空行,还是在文件之间放置三个空行(一个在开头,两个在结尾),所以我不确定到底是什么适当的替代品是。无论如何,解决所有这些问题后我可以解决以下问题:As others have pointed out, using cat or awk instead of a read-echo loop is a much better way to do this -- avoids the whitespace-trimming problem (and a couple of others you haven't stumbled upon), runs faster, and at least with cat, is simply cleaner code. Nonetheless, I'd like to take a stab at getting the read-echo loop to work right.
First, the whitespace-trimming problem: the read command automatically trims leading and trailing whitespace; this can be fixed by changing its definition of whitespace by setting the IFS variable to blank. Also, read assumes that a backslash at the end of the line means the next line is a continuation, and should be spliced together with this one; to fix this, use its -r (raw) flag. The third problem here is that many implementations of echo interpret escape sequences in the string (e.g. they may turn \n into an actual newline); to fix this, use printf instead. Finally, just as a general scripting hygiene rule, you shouldn't use cat when you don't actually need to; use input redirection instead. With those changes, the inner loop looks like this:
...there are also a couple of other problems with the surrounding script: the line that tries to define FILES as the list of available .textile files has quotes around it, meaning it never gets expanded into an actual list of files. The best way to do this is to use an array:
(and all occurrences of $f should be in double-quotes in case any of the filenames have spaces or other funny characters in them -- should really do this with $OUTPUT as well, though since that's defined in the script it's actually safe to leave off.)
Finally, there's a
echo "">$OUTPUT
near the top of the loop-over-files that's going to erase the output file every time through (i.e. at the end, it only contains the last .textile file); this needs to be moved to before the loop. I'm not sure if the intent here was to put a single blank line at the beginning of the file, or three blank lines between files (and one at the beginning and two at the end), so I'm not sure exactly what the appropriate replacement is. Anyway, here's what I can up with after fixing all of these problems:而不是:
这样做:(
如果有原因您需要逐行执行操作,最好将其包含在问题中。)
Instead of:
Do this:
(If there's a reason you need to do things line by line it'd be good to include that in the question.)
这是一种过于昂贵的组合文件方式。
如果您想在连接时向每个文件添加空白(换行符),请使用 awk
或
that's an overly expensive way of combining files.
if you want to add a blank( newline) to each file as you concatenate, use awk
OR
这允许您在每个输入文件之间散布换行符,就像您在原始脚本中所做的那样:
请注意,为了使其工作,
$FILES
未加引号(否则额外的换行符仅在所有输入文件的末尾出现一次)输出),但必须用引号括起来$f
以保护文件名中的空格(如果存在)。This allows you to intersperse newlines between each input file as you have done in your original script:
Note that
$FILES
is unquoted for this to work (otherwise the extra newlines appear only once at the end of all the output), but$f
must be quoted to protect spaces in filenames, if they exist.在我看来,正确的答案是这个,复制如下:
请注意,它会处理通过管道传输输入的情况来自另一个命令,而不仅仅是来自实际文件。
请注意,您还可以简化重定向,如下所示。
The correct answer, imo, is this, reproduced below:
Note that it'll take care of situations where the input is piped from another command, and not just from an actual file.
Note that you can also simplify the redirection as shown below.