将符合特定模式的所有行放在文件末尾

将符合特定模式的所有行放在文件末尾

我有许多如下所示的 .txt 文件:

XXXXXXXXXXXXXX
NNNNNNNNNNNNNN
NNNNNNNNNNNNNN
NNNNNNNNNNNNN
NNNNNNNNNNNN
NNNNNNNNNNN
XXXXXXXXXXXXXX
XXXXXXXXXXXXX
XXXXXXXXXXXXX
XXXXXXXXXXXX
NNNNNNNNNNNNN
NNNNNNNNNNNN
NNNNNNNNNNNNNN
NNNNNNNNNNNNNN

是否可以在 bash 中发出命令,使所有以 N 开头的行都以文件末尾结尾。所以我期望得到这样的结果:

XXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
NNNNNNNNNNNNNNNN
NNNNNNNNNNNNNNNNNN
NNNNNNNNNNNNNNNNNNNN
NNNNNNNNNNNNNNNNNNN
NNNNNNNNNNNNNNNNNN

..并对一个文件夹中的所有文件执行此操作。

答案1

你可以使用这个:

{
    grep -v '^N' file.txt
    grep '^N' file.txt
} > file.txt.temp
mv file.txt.temp file.txt

其工作原理是匹配所有不以“N”开头的行(grep -v '^N'),然后匹配所有以“N”开头的行(grep '^N'),输出到临时文件中,然后用临时文件替换原始文件。

然后如果您想对目录中的所有 *.txt 文件使用它:

for f in *.txt; do
    {
        grep -v '^N' "$f"
        grep '^N' "$f"
    } > "$f".temp
    mv "$f".temp "$f"
done

答案2

以下是使用awk

首先,KISS 版本 - 使用几个临时文件ab

for f in *.txt; do 
  awk '/^N/ {print > "b"; next} {print > "a"}' "$f"
  cat a b > "$f"
done

只是因为…

您真正想要做的是使用自定义排序顺序进行就地排序(以N所有其他内容之后的行开头,然后是原始顺序),例如在 GNU 中awk

function mycmp(ia, a, ib, b) {
  x = substr(a,1,1) == "N"
  y = substr(b,1,1) == "N"
  if (x && !y) return 1
  else if (!x && y) return -1
  else return ia - ib
}

{a[FNR] = $0}

ENDFILE {
  for (i in a) print a[i]
}

较新版本gawk提供了就地扩展 - 借用了16.7.4 启用就地文件编辑 我们可以将它们组合成一个 gawk 脚本,Nsort.awk例如:

#!/usr/bin/gawk -f

@load "inplace"

function mycmp(ia, a, ib, b) {
  x = substr(a,1,1) == "N"
  y = substr(b,1,1) == "N"
  if (x && !y) return 1
  else if (!x && y) return -1
  else return ia - ib
}

BEGIN {
  inplace = 1
  INPLACE_SUFFIX = ".bak"
  PROCINFO["sorted_in"] = "mycmp"
}

BEGINFILE {
    if (_inplace_filename != "")
        inplace_end(_inplace_filename, INPLACE_SUFFIX)
    if (inplace)
        inplace_begin(_inplace_filename = FILENAME, INPLACE_SUFFIX)
    else
        _inplace_filename = ""
}

{a[FNR] = $0}

ENDFILE {
  for (i in a) print a[i]
}

END {
    if (_inplace_filename != "")
        inplace_end(_inplace_filename, INPLACE_SUFFIX)
}

使其可执行chmod +x Nsort.awk并运行

./Nsort.awk *.txt

答案3

GNU awk(版本 4.1.0 及更高版本)

awk -i inplace '/^N/{ printlast = printlast $0 RS ; next } { print $0 } ENDFILE { printf "%s",printlast ; printlast="" }' file1 file2 file3
  1. 用于-i inplace用重新排序的文件替换原始文件。 警告:这将覆盖设置为只读的文件。
  2. 如果某一行以 开头N,则将该行附加到缓冲区printlast
  3. 否则,打印该行。
  4. 在文件末尾,打印缓冲区printlast,然后为下一个文件重置它。
  5. 指定您想要的任意数量的输入文件(例如,a.txt b.txt c.txt*.txt)。

替换-i inplace-i inplace -v INPLACE_SUFFIX=".bak"以保留每个原始文件的副本。在此示例中,备份副本文件名将全部以 结尾.bak

其他版本的 awk

如果您的版本awk无法就地编辑文件,并且您不想创建临时文件,那么您可以awk通过管道sponge(从moreutils包中)传输输出:

ls -1 *.txt | \
while read file
do
    chmod +w "${file}"
    awk '/^N/{ printlast = printlast $0 RS ; next } { print $0 } ENDFILE { printf "%s",printlast }' "${file}" | \
    sponge "${file}"
done

您需要使输入文件可写——chmod +w "${file}"以便sponge修改它们。

相关内容