如何使用 bash 仅拆分文本文件的某些元素?
How can I use bash to split only some elements of a text file?
我正在尝试弄清楚如何制作 ID 和基因的 .txt 文件 (myGeneFile.txt),如下所示:
Probe Set ID Gene Symbol
1007_s_at DDR1 /// MIR4640
1053_at RFC2
117_at HSPA6
121_at PAX8
1255_g_at GUCA1A
1294_at MIR5193 /// UBA7
进入这个:
DDR1
MIR4640
RFC2
HSPA6
PAX8
GUCA1A
MIR5193
UBA
首先我尝试这样做:
cat myGeneFile.txt | tail -n +2 | awk '{split(,a,"///"); print a[1] "\t" a[2] "\t" a[3] "\t" a[4] "\t" a[5];}' > test.txt
(即,我删除了文件的顶部 (header) 行,我尝试沿分隔符 /// 拆分第二行,然后打印可能出现的任何基因)
然后,我尝试这样做:
cat myGeneFile.txt | tail -n +2 | awk '{print }' | grep -o -E '\w+' > test.txt
(字面上列出了第二列中的所有单词)
我在这两种情况下得到了相同的输出——每行中只有第一个基因的一长串(例如,MIR4640 和 UBA7 丢失了)
有什么想法吗?
编辑:感谢@CodeGnome 的帮助。我最终使用了该代码并对其进行了修改,因为我发现我的文件每一行都有 1 到 30 个不同的基因名称。所以,我用了:
awk 'NR == 1 {next}
{
sub("///", "")
print }
{ for (i=3; i<=30; i++)
if ($i) {print $i}
}' myGeneFile.txt > test2.txt
@GlenJackson 也有一个非常有效的解决方案:
awk 'NR>1 {for (i=2; i<=NF; i++) if ($i != "///") print $i}' file
这会起作用:
tail -n+2 tmp | sed -E 's/ +/ /' | cut -d' ' -f2- | sed 's_ */// *_\n_'
这是正在发生的事情:
tail -n+2
去掉 header
sed -E 's/ +/ /'
凝白space
cut -d' ' -f2-
使用 cut 到 select 除第一个字段之外的所有字段,使用单个 space 作为分隔符
sed 's_ */// *_\n_'
将所有 ///
(以及任何周围的白色space)转换为换行符
您不需要初始 cat
,通常最好将输入文件作为参数传递给第一个命令。如果你想把文件名放在一个容易更改的地方,这是一个更好的选择,因为它避免了额外的过程(而且我发现如果它在最后更改文件更容易):
(tail -n+2 | sed -E 's/ +/ /' | cut -d' ' -f2- | sed 's_ */// *_\n_') < tmp
在 AWK 操作中使用条件打印语句
下面通过使用 sub() 删除不需要的字符来提供所需的输出,然后使用多个打印语句创建换行符。第二个打印语句是有条件的,只有当第三个字段不为空时才会触发;这避免了在输出中创建无关的空行。
$ awk 'NR == 1 {next}
{
sub("///", "")
print
if () {print }
}' myGeneFile.txt
DDR1
MIR4640
RFC2
HSPA6
PAX8
GUCA1A
MIR5193
UBA7
鉴于现有的输入和修改后的要求(来自对 Morgen 回答的评论),以下内容应该可以满足您的要求(对于任意数量的基因列)。
awk 'NR > 1 {
p=0
for (i = 2; i <= NF; i++) {
if ($i == "///") {
p=1
continue
}
printf "%s%s\n", p?"n":"", $i
}
}' input.txt
我的 awk 采取:
awk 'NR>1 {for (i=2; i<=NF; i++) if ($i != "///") print $i}' file
或 sed
sed '
1d # delete the header
s/[[:blank:]]\+/ /g # squeeze whitespace
s/^[^ ]\+ // # remove the 1st word
s| ///||g # delete all "///" words
s/ /\n/g # replace spaces with newlines
' file
您选择输出哪些字符串的标准并不完全清楚,但这里有另一个命令至少会产生您预期的输出:
tail -n +2 myGeneFile.txt | grep -oE '\<[A-Z][A-Z0-9]*\>'
它基本上只是 1) 跳过第一行和 2) 找到所有其他完全由大写字母或数字组成的单词(由行的非单词字符 and/or start/end 分隔),第一个是字母。
我正在尝试弄清楚如何制作 ID 和基因的 .txt 文件 (myGeneFile.txt),如下所示:
Probe Set ID Gene Symbol
1007_s_at DDR1 /// MIR4640
1053_at RFC2
117_at HSPA6
121_at PAX8
1255_g_at GUCA1A
1294_at MIR5193 /// UBA7
进入这个:
DDR1
MIR4640
RFC2
HSPA6
PAX8
GUCA1A
MIR5193
UBA
首先我尝试这样做:
cat myGeneFile.txt | tail -n +2 | awk '{split(,a,"///"); print a[1] "\t" a[2] "\t" a[3] "\t" a[4] "\t" a[5];}' > test.txt
(即,我删除了文件的顶部 (header) 行,我尝试沿分隔符 /// 拆分第二行,然后打印可能出现的任何基因)
然后,我尝试这样做:
cat myGeneFile.txt | tail -n +2 | awk '{print }' | grep -o -E '\w+' > test.txt
(字面上列出了第二列中的所有单词)
我在这两种情况下得到了相同的输出——每行中只有第一个基因的一长串(例如,MIR4640 和 UBA7 丢失了)
有什么想法吗?
编辑:感谢@CodeGnome 的帮助。我最终使用了该代码并对其进行了修改,因为我发现我的文件每一行都有 1 到 30 个不同的基因名称。所以,我用了:
awk 'NR == 1 {next}
{
sub("///", "")
print }
{ for (i=3; i<=30; i++)
if ($i) {print $i}
}' myGeneFile.txt > test2.txt
@GlenJackson 也有一个非常有效的解决方案:
awk 'NR>1 {for (i=2; i<=NF; i++) if ($i != "///") print $i}' file
这会起作用:
tail -n+2 tmp | sed -E 's/ +/ /' | cut -d' ' -f2- | sed 's_ */// *_\n_'
这是正在发生的事情:
tail -n+2
去掉 headersed -E 's/ +/ /'
凝白spacecut -d' ' -f2-
使用 cut 到 select 除第一个字段之外的所有字段,使用单个 space 作为分隔符sed 's_ */// *_\n_'
将所有///
(以及任何周围的白色space)转换为换行符
您不需要初始 cat
,通常最好将输入文件作为参数传递给第一个命令。如果你想把文件名放在一个容易更改的地方,这是一个更好的选择,因为它避免了额外的过程(而且我发现如果它在最后更改文件更容易):
(tail -n+2 | sed -E 's/ +/ /' | cut -d' ' -f2- | sed 's_ */// *_\n_') < tmp
在 AWK 操作中使用条件打印语句
下面通过使用 sub() 删除不需要的字符来提供所需的输出,然后使用多个打印语句创建换行符。第二个打印语句是有条件的,只有当第三个字段不为空时才会触发;这避免了在输出中创建无关的空行。
$ awk 'NR == 1 {next}
{
sub("///", "")
print
if () {print }
}' myGeneFile.txt
DDR1
MIR4640
RFC2
HSPA6
PAX8
GUCA1A
MIR5193
UBA7
鉴于现有的输入和修改后的要求(来自对 Morgen 回答的评论),以下内容应该可以满足您的要求(对于任意数量的基因列)。
awk 'NR > 1 {
p=0
for (i = 2; i <= NF; i++) {
if ($i == "///") {
p=1
continue
}
printf "%s%s\n", p?"n":"", $i
}
}' input.txt
我的 awk 采取:
awk 'NR>1 {for (i=2; i<=NF; i++) if ($i != "///") print $i}' file
或 sed
sed '
1d # delete the header
s/[[:blank:]]\+/ /g # squeeze whitespace
s/^[^ ]\+ // # remove the 1st word
s| ///||g # delete all "///" words
s/ /\n/g # replace spaces with newlines
' file
您选择输出哪些字符串的标准并不完全清楚,但这里有另一个命令至少会产生您预期的输出:
tail -n +2 myGeneFile.txt | grep -oE '\<[A-Z][A-Z0-9]*\>'
它基本上只是 1) 跳过第一行和 2) 找到所有其他完全由大写字母或数字组成的单词(由行的非单词字符 and/or start/end 分隔),第一个是字母。