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)
如何将其扩展到多个文件?欢迎任何单线解决方案.
如果要编辑文件,可以使用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
.
如果要对输入文件进行可靠的就地更新,请使用gniourf_gniourf的出色ed
回答
如果您有GNU sed
并想一次就地更新多个文件,请使用
@potong sed
基于GNU的出色回答(请参阅下文,提供可移植的替代方法,并在底部提供说明)。
注意:ed
真正更新现有文件,而sed
的-i
选项在幕后创建一个临时文件,然后替换原始文件 -虽然通常不是问题,但这可能会有不希望的副作用,最明显的是,用常规文件替换符号链接(相反,文件权限被正确保留)。
下面是POSIX兼容的外壳的功能是包装两个答案。
Stdin / stdout处理,基于@potong的出色答案:
sed
不支持-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
指定在指定为操作数的文件(f1
,f2
,fn
)更新到位,没有备份,因为没有一个备份文件可选后缀毗连到该-i
选项。
10,15!b
表示所有不(!
)的行均应10
通过15
(b
)隐式地分支到脚本的末尾(假设没有目标标签名称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
),从而有效地交换了范围中的第一行和最后一行。最后,打印修改后的图案空间。如您所见,只有跨越两行要交换的行范围才保留在内存中,而所有其他行都是单独传递的,这使此方法的存储效率更高。
这可能对你有用(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
)。
归档时间: |
|
查看次数: |
3514 次 |
最近记录: |