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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1648055/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 21:20:05  来源:igfitidea点击:

Preserving leading white space while reading>>writing a file line by line in bash

bashparsingtext-filescat

提问by Joel Hooks

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.

诚然,我是一个 bash 菜鸟,但在搜索了高低之后,我找不到合适的解决方案。显然 BASH 普遍讨厌领先的空白。

采纳答案by Laurence Gonsalves

Instead of:

代替:

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

Do this:

做这个:

cat $f >>$OUTPUT

(If there's a reason you need to do things line by line it'd be good to include that in the question.)

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

回答by Gordon Davisson

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.

正如其他人指出的那样,使用 cat 或 awk 而不是 read-echo 循环是一个更好的方法——避免空白修剪问题(以及其他一些你没有偶然发现的问题),运行速度更快,至少对于 cat,它只是更简洁的代码。尽管如此,我还是想尝试让 read-echo 循环正常工作。

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:

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

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

...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:

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

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

(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.)

(并且所有出现的 $f 都应该用双引号引起来,以防任何文件名中有空格或其他有趣的字符——也应该用 $OUTPUT 来做到这一点,尽管因为它是在脚本中定义的,所以实际上是安全的离开。)

Finally, there's a echo "">$OUTPUTnear 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:

最后,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

回答by ghostdog74

that's an overly expensive way of combining files.

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

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

if you want to add a blank( newline) to each file as you concatenate, use awk

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

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

OR

或者

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

回答by Paused until further notice.

This allows you to intersperse newlines between each input file as you have done in your original script:

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

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

Note that $FILESis unquoted for this to work (otherwise the extra newlines appear only once at the end of all the output), but $fmust be quoted to protect spaces in filenames, if they exist.

请注意,这$FILES是不加引号的(否则额外的换行符仅在所有输出的末尾出现一次),但$f必须加引号以保护文件名中的空格(如果存在)。

回答by akhan

The correct answer, imo, is this, reproduced below:

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

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

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.

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

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