Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash 如果下一行小于某个长度,则删除文本文件中的换行符_Bash_Perl_Awk_Sed - Fatal编程技术网

Bash 如果下一行小于某个长度,则删除文本文件中的换行符

Bash 如果下一行小于某个长度,则删除文本文件中的换行符,bash,perl,awk,sed,Bash,Perl,Awk,Sed,我想用bash、sed、awk或perl的任意组合创建一个脚本,如果下一行小于某个长度,它将删除一行的换行符。假设我们想要删除换行符,如果下一行少于5个字符。如果我们有此源文本文件: hi hi hi hi hi bye fun fun fun fun fun batman shirt shirt shirt pants pants pants belt paper paper paper 以下是所需的输出: hi hi hi hi hibye fun fun fun fun fun batm

我想用bash、sed、awk或perl的任意组合创建一个脚本,如果下一行小于某个长度,它将删除一行的换行符。假设我们想要删除换行符,如果下一行少于5个字符。如果我们有此源文本文件:

hi hi hi hi hi
bye
fun fun fun fun fun
batman
shirt shirt shirt
pants pants pants
belt
paper paper paper
以下是所需的输出:

hi hi hi hi hibye
fun fun fun fun fun
batman
shirt shirt shirt
pants pants pantsbelt
paper paper paper
下面是一个脚本,用于标识少于5个字符的所有行:

cat source.txt | awk 'length($0) < 5 { print NR }'
下面是一个脚本,它去掉了换行符,是前一个脚本的行号减去一:

perl -pe 'chomp if $.==1||$.==6' source.txt
如何组合这两个脚本?还是有更好的办法解决这个问题

更新

有多个正确答案,其中一些在我的Mac上不起作用,但我认为它们在其他机器上也能起作用。下面是在我的机器上用769811行CSV文件删除换行符的时间

Ed Morton的awk解决方案:23.7秒 wolfrevokcats perl与slurp:4.5秒 John1024的解决方案在我的Mac上不起作用,但我认为它在其他操作系统上起作用 ikegami的perl不含slurp:在7分钟后终止任务
就像在生活中一样,在软件中,根据已经发生的事情而不是将要发生的事情来做事情要容易得多。如果下一行包含Y,不要认为任何问题需要执行X,如果当前行包含Y,则认为需要执行Z,然后解决方案总是简单而明显的,例如:

$ cat tst.awk
NR>1{ printf "%s%s", prev, (length() < 5 ? "" : ORS) }
{ prev = $0 }
END{ print prev }

$ awk -f tst.awk file
hi hi hi hi hibye
fun fun fun fun fun
batman
shirt shirt shirt
pants pants pantsbelt
paper paper paper

在上面的示例中,如果当前行长度为5或更多,我们将打印一个换行符。它清晰、简单,可以在任何UNIX设备的任何shell中使用任何awk。

就像在生活中一样,在软件中,根据已经发生的事情而不是将要发生的事情来做事情要容易得多。如果下一行包含Y,不要认为任何问题需要执行X,如果当前行包含Y,则认为需要执行Z,然后解决方案总是简单而明显的,例如:

$ cat tst.awk
NR>1{ printf "%s%s", prev, (length() < 5 ? "" : ORS) }
{ prev = $0 }
END{ print prev }

$ awk -f tst.awk file
hi hi hi hi hibye
fun fun fun fun fun
batman
shirt shirt shirt
pants pants pantsbelt
paper paper paper
perl -p0777e "s{\r?\n(?=.{0,5}$)}{}mg" test.txt
在上面的示例中,如果当前行长度为5或更多,我们将打印一个换行符。它清晰简单,可以在任何UNIX机器上的任何shell中使用任何awk

perl -p0777e "s{\r?\n(?=.{0,5}$)}{}mg" test.txt
输出

[我花了2分钟写了一行,花了大约一个小时来解释。]

解释如下:

开关:

-p-读取输入文件的每一行,为每一行运行由-e指定的代码,并打印由-e代码修改的变量$

-0[八进制数]-输入线分隔符;如果我们指定0777,整个文件将被视为一行并立即读取

-l-从结束处剥离输入行\n,将输出行分隔符设置为等于输入行分隔符。我把它拿走了,因为这里其实不需要它

现在,正则表达式:

s{pattern}{replacement}-在变量$\中搜索模式并用replacement替换它

图案部分:

\r?\n-匹配每个换行符。对于Unix\n就足够了,\r?-Windows下旧perl版本可能需要的可选CR匹配。实际上我认为\r\n?也可以删除

?=模式-模式的正向前瞻匹配,零宽度匹配,即不消耗字符

.{0,5}$-从零到以结尾的五个字符进行匹配

s{}{}运算符修饰符:m-多行匹配,使$match位于文本中\n所有位置之前,而不仅仅是在行的末尾。 g-全局匹配,替换文本中的每个匹配项

最后,这一切是如何运作的:

Perl对整个文件-0777和-p进行slurp,然后搜索每个\r?\n出现的字符,该字符后跟不超过5个非换行字符和一个换行:?=.{0,5}$。 每次出现都被空字符串{}替换

我想我已经够清楚了

其他信息可从以下网址获得:perldoc perlre、perldoc perlop、perldoc perlrun

输出

[我花了2分钟写了一行,花了大约一个小时来解释。]

解释如下:

开关:

-p-读取输入文件的每一行,为每一行运行由-e指定的代码,并打印由-e代码修改的变量$

-0[八进制数]-输入线分隔符;如果我们指定0777,整个文件将被视为一行并立即读取

-l-从结束处剥离输入行\n,将输出行分隔符设置为等于输入行分隔符。我把它拿走了,因为这里其实不需要它

现在,正则表达式:

s{pattern}{replacement}-在变量$\中搜索模式并用replacement替换它

图案部分:

\r?\n-匹配每个换行符。对于Unix\n就足够了,\r?-Windows下旧perl版本可能需要的可选CR匹配。实际上我认为\r\n?也可以删除

?=模式-模式的正向前瞻匹配,零宽度匹配,即不消耗字符

.{0,5}$-从零到五个字符的endi匹配 与

s{}{}运算符修饰符:m-多行匹配,使$match位于文本中\n所有位置之前,而不仅仅是在行的末尾。 g-全局匹配,替换文本中的每个匹配项

最后,这一切是如何运作的:

Perl对整个文件-0777和-p进行slurp,然后搜索每个\r?\n出现的字符,该字符后跟不超过5个非换行字符和一个换行:?=.{0,5}$。 每次出现都被空字符串{}替换

我想我已经够清楚了

其他信息可从以下网址获得:perldoc perlre、perldoc perlop、perldoc perlrun。

sed也适用于以下简单替换:

$ sed -E ':a; N; s/\n(.{,4})$/\1/; ba' source
hi hi hi hi hibye
fun fun fun fun fun
batman
shirt shirt shirt
pants pants pantsbelt
paper paper paper
工作原理:

:a

这定义了一个标签a

N

这将读入下一行,并在模式空间的当前内容后面添加一个换行符

s/\n.{,4}$/\1/

如果换行符出现在当前行结束前4个字符内,则删除该换行符

ba

如果上述替换命令导致行发生更改,则跳回到标签a

BSD/MacOs 以上内容已通过GNU sed进行了测试。对于BSD/macOS sed,请尝试:

sed -E -e :a -e N -e 's/\n(.{,4})$/\1/' -e ba source
sed也适用于以下简单替换:

$ sed -E ':a; N; s/\n(.{,4})$/\1/; ba' source
hi hi hi hi hibye
fun fun fun fun fun
batman
shirt shirt shirt
pants pants pantsbelt
paper paper paper
工作原理:

:a

这定义了一个标签a

N

这将读入下一行,并在模式空间的当前内容后面添加一个换行符

s/\n.{,4}$/\1/

如果换行符出现在当前行结束前4个字符内,则删除该换行符

ba

如果上述替换命令导致行发生更改,则跳回到标签a

BSD/MacOs 以上内容已通过GNU sed进行了测试。对于BSD/macOS sed,请尝试:

sed -E -e :a -e N -e 's/\n(.{,4})$/\1/' -e ba source

如果您希望避免发出咕噜声并向前看,一般的解决方案是尽可能多地缓冲要向前看的行。一个在这种情况下

perl -ne'
   chomp;
   if (length >= 5) {
      print "$buf\n";
   } else {
      print $buf;
   }

   $buf .= $_;

   END { print "$buf\n" if defined $buf; }
'
在这种情况下,您可以执行以下操作:

perl -pe'chomp; print "\n" if length >= 5 && $. > 1; END { print "\n" if $. }'
这两种解决方案都处理最后一行没有换行符的输入


有关用法,请参阅。

如果您希望避免发出咕噜声并向前看,一般的解决方案是缓冲尽可能多的行。一个在这种情况下

perl -ne'
   chomp;
   if (length >= 5) {
      print "$buf\n";
   } else {
      print $buf;
   }

   $buf .= $_;

   END { print "$buf\n" if defined $buf; }
'
在这种情况下,您可以执行以下操作:

perl -pe'chomp; print "\n" if length >= 5 && $. > 1; END { print "\n" if $. }'
这两种解决方案都处理最后一行没有换行符的输入


有关用法,请参阅。

您可以在OpenBSD上尝试此sed ok

sed -e '$b' -e 'N;/\n...../{P;D' -e '};y/\n/ /;s/ \([^ ]*$\)/\1/' infile

您可以在OpenBSD上尝试这个sed ok

sed -e '$b' -e 'N;/\n...../{P;D' -e '};y/\n/ /;s/ \([^ ]*$\)/\1/' infile

我完全同意你的介绍,但我想知道如果用{printf%s%s,length>=5?sep:,$0;sep=ORS}END{printf%s,sep}:或者甚至length>=5{printf%s,sep}{printf%s,$0;sep=ORS;}END{printf%s,sep}@Powers,欢迎你。我完全同意你的介绍,但我想知道,如果{printf%s%s,length>=5?sep:,$0;sep=ORS}END{printf%s,sep}:或者甚至length>=5{printf%s,sep}{printf%s,sep}{0;sep=ORS;}END{printf%s,sep}不客气。一旦你考虑了你收到的所有解决方案,看看该怎么办。效果非常好!你能补充一点解释吗?你的解释倒过来了\r?\n仅在unix上用于处理unix和Windows文件\n将在Windows上处理unix和Windows文件。当我在写关于旧版本的perl for Windows时,我实际上指的是cygwin perl,我只是一下子记不起来了。我很久以前就使用过cygwin perl,所以它就是从这里来的。我刚刚检查过:基于cygwin的perl仍然有这种怪癖。工作非常完美!你能补充一点解释吗?你的解释倒过来了\r?\n仅在unix上用于处理unix和Windows文件\n将在Windows上处理unix和Windows文件。当我在写关于旧版本的perl for Windows时,我实际上指的是cygwin perl,我只是一下子记不起来了。我很久以前就使用过cygwin perl,所以它就是从这里来的。我刚刚检查过:基于cygwin的perl仍然有这种怪癖。这在我的机器上实际上不起作用,我在Mac上。它只是打印出与输入的文本文件相同的文本文件,而没有删除任何换行符。@Powers对此表示抱歉。它在Linux上适用于我。我刚刚在回答代码的末尾添加了我希望在Mac上可以使用的代码。OP上说“如果下一行少于5个字符,请删除换行符”。@potong Good eye!我刚刚更新了不到5个字符的答案;sed脚本可能会从一次读取不超过2行的文件变为一次读取不超过2行的文件。这在我的机器上实际上不起作用,我在Mac上。它只是打印出与输入的文本文件相同的文本文件,而没有删除任何换行符。@Powers对此表示抱歉。它在Linux上适用于我。我只是在答案的末尾加了一个代码,我希望这个代码能在Mac电脑上运行。操作说明是“删除换行符”
如果下一行少于5个字符,`.@poton好眼力!我刚刚更新了不到5个字符的答案;D sed脚本可能会从一次读取文件变为不超过2行。请指定如果printf“%s\n”foobar a b c D>source.txt会发生什么情况。也就是说,输出是否应该等效于printf%s\n foobara bc d或printf%s\n foobarabcd或什么?请指定如果printf“%s\n”foobar a b c d>source.txt会发生什么情况。也就是说,输出是否应该与printf%s\n foobara bc d或printf%s\n foobarabcd等效,或者是什么?