简体   繁体   English

如何确保bash中的换行符一致

[英]How to ensure consistent newline treatment in bash

I have a simple script that fetches some plain-text file using curl, assigns it to variable; 我有一个简单的脚本,该脚本使用curl获取一些纯文本文件,并将其分配给变量; later it echos it and greps it for some lines, putting them into a temp file (I actually want to put them in an array); 稍后它回显并抓取它一些行,将它们放入一个临时文件中(我实际上想将它们放入一个数组中); then iterates thru them and does something. 然后遍历它们并执行某些操作。 I run it on one machine on Mac OS X and it mostly works. 我在Mac OS X的一台计算机上运行它,并且大多数情况下都可以运行。 However, at random, either of the following happens: 但是,随机发生以下任一情况:

  • curl output is treated as a single line, so at echo/grep stage, the thing breaks; curl输出被视为单行,因此在echo / grep阶段,东西中断了;
  • entire tmp file is treated as single line when doing echo and for loop over it (I seem to have alleviated this using "read line"), so again it breaks. 整个tmp文件在执行回显和for循环时都被视为单行(我似乎已使用“读取行”缓解了此问题),因此再次中断。

Is there any way to ensure that newlines are treated consistently, once and for all? 有什么方法可以确保一劳永逸地对待换行符? I don't even care how as long as it's always the same, I can also sed results out of one-line curl output if needed. 我什至不在乎只要保持相同的时间,我也可以根据需要从单行curl输出中提取结果。

Updated: from a different place. 更新:从另一个地方。 Logged onto machine, did some stuff, noticed a script doesn't work. 登录计算机,做了一些工作,发现脚本不起作用。 Ran some commands (result, abridged, below) to test, logged off, logged back on, behavior changed (back to the expected one). 跑一些命令(结果,节略的内容,如下)进行测试,注销,重新登录,行为更改(恢复到预期的状态)。 Note the difference between for loop output... Same happens for curl output for me where exact same command on (for all I know) exact same env treats line breaks differently. 请注意for循环输出之间的区别...对于我的curl输出来说,在完全相同的命令(据我所知)完全相同的env对待换行符方面,也会发生同样的情况。 What I want to do is to run some command at the beginning of every script that makes it 100% sure the behavior never changes. 我想做的是在每个脚本的开头运行一些命令,以使其100%确保行为永远不会改变。

[root@ip-xxx-xxx-xxx-xxx ~]# cat example.txt 
ip-xxx-xxx-xxx-xxx.ec2.internal
ip-xxx-xxx-xxx-xxx.ec2.internal
[root@ip-xxx-xxx-xxx-xxx ~]# for f in `cat example.txt`; do echo "line $f"; done
line ip-xxx-xxx-xxx-xxx.ec2.internal
ip-xxx-xxx-xxx-xxx.ec2.internal
[root@ip-xxx-xxx-xxx-xxx ~]# while read f; do echo "line $f"; done < "example.txt"
line ip-xxx-xxx-xxx-xxx.ec2.internal
line ip-xxx-xxx-xxx-xxx.ec2.internal
[root@ip-xxx-xxx-xxx-xxx ~]# logout
Connection to ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com closed.
reznor-mbp:trunk2 sergey$ ssh -i ... "root@..."
Last login: ...
[root@ip-xxx-xxx-xxx-xxx ~]# for f in `cat example.txt`; do echo "line $f"; done
line ip-xxx-xxx-xxx-xxx.ec2.internal
line ip-xxx-xxx-xxx-xxx.ec2.internal

First: Don't read lines with for; 第一:不要读取for的行; read them with a while read loop: 使用while read循环读取它们:

while read -r; do
  echo "line $REPLY"
done <example.txt

The problem with for f in $(cat example.txt) is that the output of the cat operation goes through string-splitting and glob expansion before the loop is entered, making behavior dependent on a number of variables (contents of the IFS shell setting, files in the current directory [if potentially glob-expanding names are present], various shell options impacting how globs are interpreted, etc). for f in $(cat example.txt)的问题在于,cat操作的输出在进入循环之前经过字符串拆分和glob扩展,从而使行为取决于许多变量( IFS Shell设置的内容) ,当前目录中的文件(如果存在可能的glob扩展名),影响shell解释方式的各种shell选项等)。 The while read approach will behave consistently. while read方式将始终如一。

Second -- always quote expansions for the same reason: "$line" avoids string-splitting and glob expansion (again, making the above settings and variables moot), whereas bare $line has those occur. 其次-总是出于相同的原因引用扩展名: "$line"避免字符串拆分和glob扩展(再次使上述设置和变量无意义),而裸$line会出现这种情况。

Finally, the example above uses the default REPLY variable because the shell does not trim whitespace characters (that is, characters found in $IFS ) on read under those conditions. 最后,上面的示例使用默认的REPLY变量,因为在这些条件下读取时Shell不会修剪空白字符(即$IFS找到的字符)。 This is usually unnecessary (and the same effect can be had by explicitly clearing IFS, as in while IFS= read -r line; do ... ), but since the goal is to be reproducible, we're doing it right. 这通常是不必要的(通过显式清除IFS可以达到相同的效果,就像在while IFS= read -r line; do ... ),但是由于目标是可复制的,所以我们做对了。 :) :)

Follow these practices, and you will see consistent behavior between all shells compliant with POSIX sh. 遵循这些实践,您将看到所有兼容POSIX sh的外壳之间的行为一致。 (The only major shell which does not comply with POSIX sh -- breaking compatibility by default in places where it considers the standard-mandated behavior unreasonable -- is zsh). (唯一不符合POSIX sh的主要shell(默认情况下会在认为标准行为不合理的地方破坏兼容性)是zsh)。

You mention echo . 你提到echo Do you write 你写吗

echo $multiLineVar

or 要么

echo "$multiLineVar"

In the first case, the shell will treat embedded newlines as white-space that separates words, and so echo never sees them. 在第一种情况下,外壳程序会将嵌入的换行符视为分隔单词的空格,因此echo永远不会看到它们。

Simple way is to use a while loop as below 简单的方法是使用while循环,如下所示

filename=/path/to/file/file.txt
while read variablename

do echo "$variablename" done < "filename" What it does is it reads whole line until \\n do echo "$variablename" done < "filename"它所做的是读取整行,直到\\ n

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM