我在尝试使用 while 循环和 awk
提取多行冒号后面的数据时遇到问题.
这是我的数据结构:
Identifiers:BioSample:SAMD00019077
Identifiers:BioSample:SAMD00019076
Identifiers:BioSample:SAMD00019075
Identifiers:BioSample:SAMD00019074
Identifiers:BioSample:SAMD00019073
Identifiers:BioSample:SAMD00019072
Identifiers:BioSample:SAMD00019071;SRA:DRS051563
Identifiers:BioSample:SAMD00019070;SRA:DRS051562
Identifiers:BioSample:SAMD00019069;SRA:DRS051561
...
Identifiers:BioSample:SAMD00019005;SRA:DRS051497
Identifiers:BioSample:SAMD00015713;SRA:DRS012785
我想要得到的是BioSample ID
,类似于 SAMD00019077
.
我试过的脚本:
-
while read line ; do echo $line | awk -F':' '{print $3}' > 1.tmp2 ; done < 1.tmp
-
for line in
猫 1.tmp; do echo $line | awk -F':' '{print $3}' > 1.tmp2 ; done
-
for line in
猫 1.tmp; do echo $line | awk -F: '{print $3 > "1.tmp2"}' ; done
他们只给了Biosample ID
最后一行:
$ while read line ; do echo $line |
awk -F':' '{print $3}' > 1.tmp2 ; done < 1.tmp
$ head 1.tmp2
SAMD00015713;SRA
我阅读了此处的帖子,看起来我的问题与 stdin
有关, stdout
和 stderr
.
bash read loop only reading first line of input variable
bash while loop read only one line
我试过的解决方案,它给出了 1 行的结果
$ exec 3<&1
$ exec 1<&2
$ while read line ; do echo $line |
awk -F':' '{print $3}' > 1.tmp2 ; done< 1.tmp
$ head 1.tmp2
SAMD00015713;SRA
$ exec 1<&3 3<&-
我也试过exec < 1.tmp
将文件指向 stdin
但它会导致错误。
我发现这些脚本对我来说效果很好。但我真的很想知道为什么我上面尝试的脚本失败了。
cat 1.tmp | awk -F: '{print $3}' | head
awk -F: '{print $3}' 1.tmp | head
最佳答案
首先,awk
具有循环遍历行的能力,字段分隔符可以是正则表达式。
因此,您的脚本可以简化为这种优化格式:
awk -F'[;:]' '{print $3}' 1.tmp > 1.tmp2
这是您可以使用的优化格式。
话虽如此,您可能想知道脚本中出了什么问题。
while read line ; do echo $line | awk -F':' '{print $3}' > 1.tmp2 ; done < 1.tmp
^ here
上面标记的>
就是重定向操作符。
它将命令的标准输出(在本例中为 awk
)写入指定的文件。它不追加,而是覆盖。
因此,在循环的每次迭代中,都会清除文件并将命令的输出写入其中。因此它只留下最后一个条目。
要解决这个问题,您可以使用附加重定向:>>>
。
while read line ; do echo $line | awk -F':' '{print $3}' >> 1.tmp2 ; done < 1.tmp
现在,有一个警告。如果文件原本不是空的怎么办?此循环将附加到文件,而不先清除文件。要解决此问题,您可以先使用以下命令清除文件:
>1.tmp2; while read line ; do echo $line | awk -F':' '{print $3}' >> 1.tmp2 ; done < 1.tmp
但是,如果我们确定循环产生的所有标准输出都需要进入文件,您可以简单地将重定向移出循环。这样,shell 就不必一直打开和关闭文件描述符。
while read line ; do echo $line | awk -F':' '{print $3}'; done < 1.tmp > 1.tmp2
请注意,这些选项未经优化,但仍然有效。优化的选项是让 awk
本身按照答案的第一个片段中提到的那样进行逐行处理。
关于linux - Bash循环只读最后一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57423117/