unix one-liner在多个文本文件中交换/转置两行?

Rog*_*ger 3 unix bash awk sed text-files

我希望使用诸如sed或awk之类的UNIX工具根据它们的行号(例如,将行10和15的位置切换)在多个文本文件中交换或转置成对的行.

例如,我相信这个sed命令应该在一个文件中交换第14行和第26行:

sed -n '14p' infile_name > outfile_name
sed -n '26p' infile_name >> outfile_name
Run Code Online (Sandbox Code Playgroud)

如何将其扩展到多个文件?欢迎任何单线解决方案.

gni*_*urf 8

如果要编辑文件,可以使用ed标准编辑器.你的任务很简单ed:

printf '%s\n' 14m26 26-m14- w q | ed -s file
Run Code Online (Sandbox Code Playgroud)

它是如何工作的?

  • 14m26告诉ed我们走#14行并在第26行之后移动它
  • 26-m14-告诉ed你在第26行(这是你原来的第26行)之前取线,然后在第14行之前的一行之后移动它(这是第14行的原因)
  • w告诉ed写文件
  • q告诉ed退出.

如果您的数字在变量中,您可以执行以下操作:

linea=14
lineb=26
{
    printf '%dm%d\n' "$linea" "$lineb"
    printf '%d-m%d-\n' "$lineb" "$linea"
    printf '%s\n' w q
} | ed -s file
Run Code Online (Sandbox Code Playgroud)

或类似的东西.确保这一点linea<lineb.


mkl*_*nt0 6

注意:ed真正更新现有文件sed-i选项在幕后创建一个临时文件,然后替换原始文件 -虽然通常不是问题,但这可能会有不希望的副作用,最明显的是,用常规文件替换符号链接(相反,文件权限被正确保留)。

下面是POSIX兼容的外壳的功能包装两个答案


Stdin / stdout处理,基于@potong的出色答案

  • POSIXsed不支持-i就地更新。
  • 它还不支持\n在字符类内部使用,因此[^\n]必须替换为一个积极定义所有字符(除了\n可能出现在一行上的字符)的繁琐解决方法-这是通过将可打印字符与所有(ASCII)控件结合在一起的字符类实现的不\n包括在文字中的字符(通过使用命令替换printf)。
  • 还要注意需要将sed脚本分成两个-e选项,因为POSIX sed要求分支命令(b在这种情况下)以实际的换行符终止或在单独的-e选项中继续。
# SYNOPSIS
#   swapLines lineNum1 lineNum2
swapLines() {
  [ "$1" -ge 1 ] || { printf "ARGUMENT ERROR: Line numbers must be decimal integers >= 1.\n" >&2; return 2; }
  [ "$1" -le "$2" ] || { printf "ARGUMENT ERROR: The first line number ($1) must be <= the second ($2).\n" >&2; return 2; }
  sed -e "$1"','"$2"'!b' -e ''"$1"'h;'"$1"'!H;'"$2"'!d;x;s/^\([[:print:]'"$(printf '\001\002\003\004\005\006\007\010\011\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177')"']*\)\(.*\n\)\(.*\)/\3\2\1/'
}
Run Code Online (Sandbox Code Playgroud)

例:

$ printf 'line 1\nline 2\nline 3\n' | swapLines 1 3 
line 3
line 2
line 1
Run Code Online (Sandbox Code Playgroud)

基于gniourf_gniourf的出色回答就地更新

小警告:

  • 尽管ed它是POSIX实用程序,但并非所有平台上预装了它,特别是在Windows的Debian以及Cygwin和MSYS Unix仿真环境中并未预装。
  • ed始终读取输入文件作为一个整体到内存中。
$ printf 'line 1\nline 2\nline 3\n' | swapLines 1 3 
line 3
line 2
line 1
Run Code Online (Sandbox Code Playgroud)

例:

$ printf 'line 1\nline 2\nline 3\n' > file
$ swapFileLines 1 3 file
$ cat file
line 3
line 2
line 1
Run Code Online (Sandbox Code Playgroud)

一种解释@波东的GNU sed为基础的答案

他的命令交换第10和15行:

sed -ri '10,15!b;10h;10!H;15!d;x;s/^([^\n]*)(.*\n)(.*)/\3\2\1/' f1 f2 fn
Run Code Online (Sandbox Code Playgroud)
  • -r激活对扩展正则表达式的支持;在这里,值得注意的是,它允许使用未转义的括号来形成捕获组
  • -i指定在指定为操作数的文件(f1f2fn)更新到位没有备份,因为没有一个备份文件可选后缀毗连到该-i选项。

  • 10,15!b表示所有!)的行均应10通过15b)隐式地分支到脚本末尾(假设没有目标标签名称b),这意味着这些行将跳过以下命令。实际上,它们只是按原样打印

  • 10h将(h)行号10(范围的开头)复制到所谓的保持空间,该空间是辅助缓冲区。
  • 10!H H不是行的每一行(10在这种情况下,这意味着11通过行)附加到()15到保留空间。
  • 15!d 删除d)每一个被线排队15(这里,线10通过14)分支和所述脚本的末尾(跳过其余的命令)。通过删除这些行,它们不会被打印。
  • x,这是仅适用于线执行15(该范围的端部),代替所谓的模式空间与保持空间,其在该点保持在范围内的所有行(的内容10通过15); 模式空间是sed操作命令的缓冲区,缺省情况下,其内容是打印出来的(除非-n指定了)。
  • s/^([^\n]*)(.*\n)(.*)/\3\2\1/然后使用捕获组(构成传递给function的第一个参数的正则表达式的括号化子表达式s)将模式空间的内容划分为第一行(^([^\n]*)),中间行((.*\n))和最后一行((.*)),然后在替换字符串(传递给function的第二个参数s)中,使用后向引用将最后一行(\3)放在中间行()之前\2,然后是第一行(\1),从而有效地交换了范围中的第一行和最后一行。最后,打印修改后的图案空间。

如您所见,只有跨越两行要交换的行范围才保留在内存中,而所有其他行都是单独传递的,这使此方法的存储效率更高。


pot*_*ong 5

这可能对你有用(GNU sed):

sed -ri '10,15!b;10h;10!H;15!d;x;s/^([^\n]*)(.*\n)(.*)/\3\2\1/' f1 f2 fn
Run Code Online (Sandbox Code Playgroud)

这将在保持空间中存储一系列行,然后在该范围完成后交换第一行和最后一行。

i标志在适当的位置编辑每个文件 ( f1, f2... fn)。

  • 哎呀,错误地留下了换行符。编辑了解决方案,因此适用于连续行。 (3认同)