仅使用Shell脚本从文本文件获取特定行


100

我正在尝试从文本文件中获取特定行。

到目前为止,在网上我只看到sed之类的东西(我只能使用sh-不能使用bash或sed或类似的东西)。我只需要使用基本的shell脚本来执行此操作。

cat file | while read line
    do
       #do something
    done

我知道如何遍历行,如上所示,但是如果我只需要获取特定行的内容怎么办


你知道电话号码吗?
Mehul Rathod

1
然后,您就可以数了。
伊格纳西奥·巴斯克斯

是的,行号是5 @MehulRathod
GangstaGraham

3
为什么还cat可以,但sed不可以?这是没有意义的。
威廉·珀塞尔

5
因为没有人可以拒绝cat。真可爱cat

Answers:


204

sed:

sed '5!d' file

awk:

awk 'NR==5' file

关于sh命令,我不能使用sed,awk。我应该在问题中更清楚地说明这一点。
GangstaGraham 2013年

@GangstaGraham,您说过您知道如何遍历行,添加计数器又如何呢?如果计数器达到您的目标行号,请获取该行并中断循环。有帮助吗?
肯特,

4
@KanagaveluSugumar阅读了sed的信息页。5!d表示删除除5外的所有行。shell var是可能的,您需要双引号。
肯特2014年

13
我建议添加另一个变体:sed -n 5p对于新手来说,这似乎更合乎逻辑,因为它的-n意思是“默认情况下不输出”,p代表“打印”,并且没有潜在的混淆性提及删除(当人们谈论文件时,删除行往往会表示不同的意思)。
Josip Rodin

1
@JosipRodin你是对的,也-n '5p'可以解决此问题。区别在于,5!d您可以添加-i将更改写回到文件中。但是,对于这个问题,-n 5p您不得不sed -n '5p' f > f2&& mv f2 f再次提出,我同意您的意见。
肯特2015年

21

假设line是一个变量,该变量保存您所需的行号,如果可以使用headtail,那么它很简单:

head -n $line file | tail -1

如果没有,这应该起作用:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done

这种-eq比较是针对整数,因此它需要一个行号,而不是行内容($line)。必须通过want=5在循环之前定义(例如)然后在-eq上使用比较来解决此问题$want。[
转自

1
@JosipRodin我同意您的意见,因此提出了一个独立的编辑建议。希望这次不会被拒绝。
Victor Zamanian '17

15

您可以使用sed -n 5p file

您还可以获取一个范围,例如sed -n 5,10p file


11

最佳表现方法

sed '5q;d' file

因为sed在第五行之后停止读取任何行

Roger Dueck先生的最新实验

我安装了wcanadian-insane(6.6MB),并比较了sed -n 1p / usr / share / dict / words和sed'1q; d'/ usr / share / dict / words;第一个花费了0.043s,第二个花费了0.002s,因此使用'q'绝对是性能上的提高!


1
这通常也是这样写的:sed -n 5q
William Pursell,2015年

3
我喜欢这种解决方案,因为sed在第5行之后停止读取任何行。
Anthony Geoghegan

1
我安装了wcanadian-insane(6.6MB)sed -n 1p /usr/share/dict/wordssed '1q;d' /usr/share/dict/words使用time命令进行了比较;第一个花费了0.043s,第二个花费了0.002s,因此使用'q'绝对是性能上的提高!
罗杰·迪克

5

例如,如果要获取文件的第10至20行,则可以使用以下两种方法:

head -n 20 york.txt | tail -11

要么

sed -n '10,20p' york.txt 

p 在上面的命令代表打印。

这是您将看到的: 在此处输入图片说明


2

做这种事情的标准方法是使用外部工具。编写shell脚本时禁止使用外部工具是荒谬的。但是,如果您确实不想使用外部工具,则可以使用以下命令打印第5行:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

请注意,这将打印逻辑行5。也就是说,如果input-file包含行连续,则它们将被计为单行。您可以通过添加-r到read命令来更改此行为。(这可能是所需的行为。)


1
$((++i))似乎是一种bashism;如果OP受限于使用外部工具,则我不认为他们将拥有比普通工具更多的访问权/bin/sh
Josip Rodin

@JosipRodin否,这是POSIX功能(但对++增量的支持专门标记为可选)。
三人房

@tripleee它不适用于现代破折号/ bin / sh,所以我不会依赖它。
Josip Rodin

但是,一个简单的解决方法也$((i+=1))可以在Dash中使用。
2015年

$(($i+1))是我所想到的简单解决方法。
Josip Rodin

1

William Pursell的答案并行是,这是一个简单的构造,即使在原始的v7 Bourne shell中(也因此在没有Bash的地方),该构造也应工作。

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

break当我们获得所需的行时,还要注意对循环外的优化。


0

我不特别喜欢任何答案。

这是我的方法。

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"

-1

使用perl轻松!如果要从文件获取第1、3和5行,请说/ etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd

seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'但是smartmatch是实验性的,不鼓励使用
Sorin

如此简洁,或其他如此灵活的解决方案,都不是其他任何一种解决方案。(为什么看起来所有节省时间和使事情变得容易的事物都被“聪明的人”“
蒙蔽

-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"

3
您能否至少描述一下为什么这项工作可以使提出问题的人更清楚?
2016年

因此,第一个grep会选择所有在行首添加行号的行。然后,第二个grep通过在开始时匹配行号来选择特定行。最后,从回声开始的行开始修剪行号。
奥德

与相比sed -n 5p,这既复杂又效率低下,当然仍然可以优化为类似sed -n '5!d;p;q'
Tripleee
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.