如何使用SED/AWK在文本文件中交换第一行和最后一行?

4

我正在尝试在Unix中交换文本文件中的第一行和最后一行。 文件内容为:

line1
line2
line3
line4
line5
line6

I want like this:

line6 
line2
line3
line4
line5
line1

我正在使用sed -n -e '1 s/^.*$/$p' file,但它并没有起作用。

我不知道文件的长度,也就是说行数未知,但需要使用sed/awk将该文件的第一行替换为最后一行。 - Raghavendra S
1
尝试运行以下命令:first="$(head -n 1 file1)"; last="$(tail -n 1 file1)"; echo -e $"$last" "\n$(cat file1 | tail -n +2 | head -n -1)" $"\n$first" > file2 - Wiktor Stribiżew
6个回答

2

编辑2: 根据Ed先生的评论,在此添加这个解决方案,即使第二行为空也将起作用。

awk 'NR==1{first=$0;next} NR>2{val=val prev ORS} {prev=$0} END{print prev ORS val first}  Input_file
编辑:如果您要想只交换第一行和最后一行,下面的方法可能会有帮助(假设您的输入文件与示例相同)。
awk 'FNR==1{first=$0;next} {val=(val?val ORS prev:prev?$0:"")} {prev=$0} END{print $0 ORS val ORS first}' Input_file

解释:

awk '
FNR==1{                            ##Checking if line is first line then do following.
  first=$0;                        ##Creating varable first whose value is current line value.
  next                             ##next will skip all further statements from here.
}
{
  val=(val?val ORS prev:prev?$0:"")  ##Creating variable named val here whoe value is concatenating to its own value with variable prev value.
}
{
  prev=$0                          ##Creating variable prev whose value will be current value of line but will become previous value for next line.
}
END{
  print $0 ORS val ORS first       ##Printing current line ORS val ORS and first here.
}'  Input_file                     ##Mentioning Input_file name here.

请您尝试以下方法,如果有帮助请告知我。

awk -v from=1 -v to=6 'FNR==from{source=$0;next} FNR==to{target=$0;next} {val=val?val ORS $0:$0} END{print target ORS val ORS source}' Input_file

2
我应该放弃并停止提醒您,在某些awk上下文中,未加括号的三元表达式会导致语法错误吗?此外,根据POSIX,END部分中$0的值是未定义行为,因此这将根据您运行的awk版本产生不同的输出 - 在END中使用prev而不是$0。 - Ed Morton
1
@EdMorton,真的非常抱歉,我现在会尝试修复它 :( - RavinderSingh13
2
@RavinderSingh13,请不要误解我的意思,我绝对不是在下命令或期望你听从我,我只是想帮助你成为更好的awk脚本编写者。当然,如果你不想听取我的建议,那完全由你决定,如果不想听,请告诉我退后一步,我会立刻停止,没有任何问题。我是你的同行,而不是你的老板,我只是有更多使用awk的经验,仅此而已。 - Ed Morton
1
其实,这是我编写脚本来完成您想要的操作的方式:awk 'NR==1{first=$0;next} NR>2{val=val prev ORS} {prev=$0} END{print prev ORS val first}' file - Ed Morton
1
@EdMorton,非常感谢您,Ed先生。我已经将您的解决方案添加到帖子中了,请像往常一样纠正和指导我 :) - RavinderSingh13
显示剩余13条评论

2

在sed中,您需要进行两次处理;使用ed可能更有意义。然后我们只需要两个move命令-将最后一行移动到第1行之后,然后将第一行移动到末尾:

$m1
1m$

演示

#!/bin/bash

set -euo pipefail

# Create our input file

file=$(mktemp)
trap 'rm "$file"' EXIT

cat >"$file" <<END
line1
line2
line3
line4
line5
line6
END

echo Before:
cat "$file"
echo

# Make the change

ed -s "$file"  <<<$'$m1\n1m$\nwq'

# Show the result

echo After:
cat "$file"

如果你需要编写不同的输出文件,当然可以像往常一样向wq命令添加文件名参数。


@Downvoter:这个回答真的很糟糕吗?我应该怎么改进它呢? - Toby Speight

2
这可能适用于你(GNU sed):
sed 'H;$!d;x;s/\n\([^\n]*\)\(\n.*\n\)\(.*\)/\3\2\1/' file

将整个文件复制到保留空间(HS)中,并在最后一行之后,切换到HS,将文件分成第一行、中间和最后一行,并替换第一行和最后一行。

另一种两遍的解决方案:

sed -n '1s/.*/$c&/p;$s/.*/1c&/p' file | sed -f - file

创建一个sed脚本,将第一行更改为最后一行,将最后一行更改为第一行。
另一个解决方案是使用cat、head、tail和sed:
cat <(tail -1 file) <(sed '1d;$d' file) <(head -1 file)

另一个sed解决方案:

sed -E '1h;1d;:a;N;$!ba;s/(.*)\n(.*)/\2\n\1/;G' file

0
使用sponge util
f=file
{ tail -1 $f ; tail -n +2 $f | head -n -1 ; head -1 $f ; } | sponge $f

0

如果需要混合使用一行代码,包括 headtailsed

$ FIRST=$(head -1 file.txt) LAST=$(tail -1 file.txt) \
sed "1 s/^.*$/${LAST}/" file.txt | sed "$ s/^.*$/${FIRST}/"

0

对于非常大的文件,您可能会对双重传递感兴趣:

awk '(NR!=FNR){print (FNR==1 ? t : (FNR==c ? h : $0) ); next }
     (NR==1){h=$0}{t=$0;c=NR}' file file

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接