我有许多如下所示的 .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 版本 - 使用几个临时文件a
和b
:
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
- 用于
-i inplace
用重新排序的文件替换原始文件。 警告:这将覆盖设置为只读的文件。 - 如果某一行以 开头
N
,则将该行附加到缓冲区printlast
。 - 否则,打印该行。
- 在文件末尾,打印缓冲区
printlast
,然后为下一个文件重置它。 - 指定您想要的任意数量的输入文件(例如,
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
修改它们。