12 command-line sed text-processing
目前,我有多个文本文件,内容如下(多行):
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15
Run Code Online (Sandbox Code Playgroud)
我希望将每一行更改为以下格式:
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
Run Code Online (Sandbox Code Playgroud)
有没有办法使用 sed 执行上述操作?还是我需要求助于 Python?
ter*_*don 22
你可以用 sed 来做,是的,但其他工具更简单。例如:
$ awk '{
printf "%s ", $2;
for(i=3;i<=NF;i++){
printf "%s:%s:1 ",$1,$(i)
}
print ""
}' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
Run Code Online (Sandbox Code Playgroud)
awk 将在空格上分割每一行输入(默认情况下),将每个字段保存为$1, $2, $N。所以:
printf "%s ", $2; 将打印第二个字段和一个尾随空格。 for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }: 将遍历字段 3 到最后一个字段(NF是字段数),并且对于每个字段,它将打印第一个字段 a :,然后是当前字段和 a :1。print "" :这只是打印一个最终的换行符。 或 Perl:
$ perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
Run Code Online (Sandbox Code Playgroud)
该-a品牌perl表现得像awk和空格分割它的输入。在这里,字段存储在数组中@F,这意味着第一个字段将是$F[0],第二个$F[1]等等。所以:
print "$F[1] " : 打印第二个字段。 print "$F[0]:$_:1 " for @F[2..$#F];: 迭代字段 3 到最后一个字段($#F是数组中的元素数@F,因此@F[2..$#F]从第 3 个元素开始到数组末尾获取数组切片)并打印第一个字段 a :,然后是当前字段和 a :1.print "\n" :这只是打印一个最终的换行符。Zan*_*nna 12
这里有一个 可怕 sed 道路!
$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
Run Code Online (Sandbox Code Playgroud)
更易读:
sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'
Run Code Online (Sandbox Code Playgroud)
-r 使用EREs/old/new/替换old为new^([0-9]+) 在行首保存一些数字\1 对第一个保存的模式的反向引用:a 标记脚本的这一部分 a( |$) 空格或行尾t 测试上次替换是否成功 - 如果成功,则执行下一个命令a找到标签,:a然后再做一次s/ $// 删除尾随空格因此,在将结构添加到第一部分之后,我们反复查找结构的最后一个实例并将其应用于下一个数字......
但我同意其他工具使它更容易......
使用 awk:
awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file
Run Code Online (Sandbox Code Playgroud)
或使用 bash:
while read -r -a a; do # read line to array a
printf "%s " ${a[1]} # print column #1
for ((i=2;i<${#a[@]};i++)); do # loop from column #2 to number of columns
printf "%s " "${a[0]}:${a[$i]}:1" # print content/values
done
echo # print line break
done < file # read file from stdin
Run Code Online (Sandbox Code Playgroud)
输出:
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
好吧,你可以在 sed 中完成,但 python 也可以。
$ ./reformatfile.py input.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
Run Code Online (Sandbox Code Playgroud)
内容reformatfile.py如下:
$ ./reformatfile.py input.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
Run Code Online (Sandbox Code Playgroud)
这是如何运作的?真的没有什么特别的事情发生。我们将第一个命令行参数作为文件打开以供阅读,然后将每一行分解为“单词”或单个项目。第一个词变成pref可变的,我们在标准输出上打印第二个 ( words[1] ) 以空格结尾的项目。接下来,我们通过列表推导式和.join()函数在 pref、每个单词和 string 的临时列表上构造新的“单词”集"1"。最后一步是打印出来