在不使用临时文件的情况下将输出的前N行移至末尾


11

想像一下命令的输出

44444
55555
11111
22222
33333

我该如何抽出前N行(在上面的示例中是前两行)并在末尾附加它们,但不使用临时文件(因此仅使用管道)?

11111
22222
33333
44444
55555

类似的东西| sed -n '3,5p;1,2p'(显然不像sed一样起作用,不在乎命令的顺序)。


2
为什么我们不能使用临时文件?
Braiam '16

Answers:


13

只需将这些行复制到保持缓冲区(然后删除它们),然后在最后一行将保持缓冲区的内容追加到模式空间:

some command | sed '1,NUMBER{           # in this range
H                                       # append line to hold space and
1h                                      # overwrite if it's the 1st line
d                                       # then delete the line
}
$G'                                     # on last line append hold buffer content

随着gnu sed你可以写成

some command | sed '1,NUMBER{H;1h;d;};$G'

这是ol'的另一种方法ed(它将r的输出填充some command到文本缓冲区中,然后在最后一个之后m保留行):1,NUMBER$

ed -s <<IN
r ! some command
1,NUMBERm$
,p
q
IN

请注意,如前所述,如果输出少于NUMBER+1行,它们都将失败。一个更可靠的方法是(gnu sed语法):

some command | sed '1,NUMBER{H;1h;$!d;${g;q;};};$G'

只要该行不是最后一行($!d),它只会删除该范围内的行-否则它会使用保持缓冲区内容(g)和quit(在打印当前模式空间之后)覆盖模式空间。


2
sed -e '1,NUMBER{H;1h;d;}' -e '$G'也可移植的工作(注意,一些sed实现不能持有超过保留空间几千字节,因此NUMBER不能过大存在)
斯特凡Chazelas

@StéphaneChazelas-谢谢您的输入-我通常每行都使用一个命令,因为我确定这是可移植的-多种表达式语法一直让我感到困惑,例如posix文档说“ <right-brace>应该是前面加上<newline>”,所以根据他们的说法不应该sed -e '1,NUMBER{H;1h;d' -e'}' -e '$G'吗?
don_crissti

4
通常,用新的-e替换换行符。d;}还不是POSIX,而是可移植的。在下一个POSIX规范中将解决此问题。参见austingroupbugs.net/view.php?id=944#c2720 –StéphaneChazelas
2016年

2
@don_crissti谢谢!如果您还可以简短说明它为何起作用,那就太好了。(当然,我会去查找它,但这将为您提供更有价值的答案。)
Peter Uhnak

在我的脑海中,不可移植件事1,NUMBER{H;1h;d;}不立即后有一个分号括号。不过,这可能只是SunOS 4.1中sed的一个错误,不过20年后,它的解决方法仍然存在我的脑海。
zwol

11

一种awk方法:

cmd | awk -v n=3 '
  NR <= n {head = head $0 "\n"; next}
  {print}
  END {printf "%s", head}'

@don_crissti的sed方法的一个好处是,如果输出的n行数或更少,它仍然可以工作(输出行)。


您可能要用\nORS 替换硬编码,以便与其他记录分隔符一起使用(例如,您要使用段落等)。
fedorqui '16

6

我有xclip,可以用这种方式完成:

./a_command | xclip -in && xclip -o | tail -n +3 && xclip -o | head -n 2

这是它的描述:

xclip - command line interface to X selections (clipboard)

NAME
       xclip - command line interface to X selections (clipboard)

SYNOPSIS
       xclip [OPTION] [FILE]...

DESCRIPTION
       Reads from standard in, or from one or more files, and makes the data available as an X selection for pasting into X applications. Prints current X selection to standard out.

       -i, -in
              read text into X selection from standard input or files (default)

       -o, -out
              prints the selection to standard out (generally for piping to a file or program)

3
为xclip的广告素材(滥用)+1。答案需要一个可访问/正在运行的X服务器。
jofel

3

Perl方式:

perl -ne '$.<3?($f.=$_):print;}{print $f'

或者,同样的东西写得不太清楚:

perl -ne 'if($.<3){ $f.=$_ } else{ print } END{print $f}'

例如:

$ cat file
44444
55555
11111
22222
33333

$ cat file | perl -ne '$.<3?($f.=$_):print;}{print $f'
11111
22222
33333
44444
55555

说明

  • -ne:逐行读取输入文件/流,然后将给定的脚本-e应用于每一行。
  • $.<3$.是当前行号,因此请更改3为要移动的行数。
  • $.<3?($f.=$_):print;:这是条件运算符,一般格式为condition ? case1 : case2case1如果condition为true和case2false ,它将运行。在此,如果当前行号小于3,则将当前行($_)附加到变量中$f;如果行号大于3,则进行打印。
  • }{ print $f}{是perl的简写END{}。处理完所有输入行后,它将运行。此时,我们将收集所有要移动的行,并打印所有我们想单独留下的行,因此打印另存为的行$f

1
关于您的高尔夫版本,可以删除一些字符perl -ne '$.<3?$f.=$_:print}{print $f
123年

1

使用POSIX ex。是的,它用于文件编辑,但是可以在管道中使用。

printf %s\\n 111 222 333 444 555 | ex -sc '1,2m$|%p|q!' /dev/stdin

它可以在管道的开头或结尾添加任意命令,并且将以相同的方式工作。更好的是,考虑到的存在/dev/stdin,它符合POSIX。

(我不知道是否/dev/stdin在POSIX中指定了,但是我看到它在Linux和Mac OS X中都存在。)

与使用sed的保留空间相比,这具有可读性上的优势-您只需告诉ex“将这些行移到末尾”即可。(其余命令的意思是“打印缓冲区”和“退出”,它们也很容易阅读。)

注意:ex如果输入少于2行,则上面给出的命令将失败。

进一步阅读:


0

简短python摘要:

#!/usr/bin/env python3
import sys
file_ = sys.argv[1]
lines = int(sys.argv[2])
with open(file_) as f:
    f = f.readlines()
    out = f[lines:] + f[:lines]
    print(''.join(out), end='')

将文件名作为第一个参数传递,并将行数作为第二个参数传递。

例:

$ cat input.txt
44444
55555
11111
22222
33333

$ ./sw_lines.py input.txt 2
11111
22222
33333
44444
55555

$ ./sw_lines.py input.txt 4
33333
44444
55555
11111
22222

0

如果您可以将整个输出存储在内存中:

data=$(some command)
n=42                  # or whatever
{ tail -n +$((n+1)) <<<"$data"; head -n $n <<<"$data"; } > outputfile

0

这是另一个需要GNU的选项sed

(x=$(gsed -u 3q);cat;printf %s\\n "$x")

-u使GNU成为sed无缓冲的,以便该sed命令不占用超过3行的STDIN。该命令替换将删除空行,以便在第三,第三和第二或第三,第二和第一行为空的情况下,该命令在输出的末尾不包含空行。

您还可以执行以下操作:

tee >(sponge /dev/stdout|sed -u 3q)|sed 1,3d

如果没有sponge /dev/stdout|该命令,则输入较长时将失败。sponge /dev/stdout也可以用代替tac|tac,即使例如a\ncb在输入是换行的a\nb\nc地方打印时,也可以用代替,即使从输入末尾删除空行也可以用代替。当输入的行数为一或两时,以上命令删除第一行。\n(x=$(cat);printf %s\\n "$x")

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.