只使用shell脚本从文本文件中获取特定行

146

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

到目前为止,我只看到过像sed这样的东西(我只能使用sh -不是bash或sed或类似的东西)。我需要使用基本的shell脚本来完成这个任务。

cat file | while read line
    do
       #do something
    done

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


1
然后你可以开始计数。 - Ignacio Vazquez-Abrams
是的,行号是5 @MehulRathod - GangstaGraham
这是我学习Shell脚本的第一周,所以我仍在学习基础知识。 - GangstaGraham
5
为什么 cat 可以,但是 sed 不行?这毫无意义。 - William Pursell
10
因为没有人能够拒绝“猫”。啊...可爱的“猫”! - user1974640
显示剩余2条评论
13个回答

265

sed:

sed '5!d' file

awk:

awk 'NR==5' file

关于sh命令,我不能使用sed、awk。我应该在问题中更明确地表达这一点。 - GangstaGraham
@GangstaGraham,你说你知道如何迭代遍历行,那加一个计数器怎么样?如果计数器达到了你想要的行数,就获取该行并退出循环。这样有帮助吗? - Kent
4
请参考sed命令的信息页面。5!d 表示删除除第五行以外的所有行。可以使用shell变量,但必须使用双引号。 - Kent
29
我建议添加另一种变体:sed -n 5p。对于新手来说,这似乎更容易记住,因为-n表示“默认情况下不输出”,而 p代表“打印”,没有可能引起混淆的删除提到(当人们谈论文件时,删除行往往意味着不同的事情)。 - Josip Rodin
3
@JosipRodin 你是对的,-n '5p' 对于这个问题也可以解决。不同之处在于,使用 5!d 可以添加 -i 将更改写回文件。然而,使用 -n 5p 你必须再次执行 sed -n '5p' f > f2&& mv f2 f,对于这个问题,我同意你的观点。 - Kent
显示剩余3条评论

36

假设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,然后在$want上使用-eq比较来修复此问题。[从被拒绝的编辑中移动] - Josip Rodin
1
@JosipRodin,我根据您的评论提出了一个独立的编辑建议,因为我同意它。希望这次不会被拒绝。 - Victor Zamanian
这比下面 @faithonour 的解决方案慢得多。 - AntonOfTheWoods
@AntonOfTheWoods,虽然使用sed显然更快,但它并不能回答这个问题。如果你错过了,问题明确说明sed不可用,而sh是唯一可用的工具。 - micromoses
没错,他确实接受了答案! :-) - AntonOfTheWoods

31

您可以使用 sed -n 5p 文件 来获取文件的第五行。

您也可以获取一个范围,例如 sed -n 5,10p 文件 可以获取文件的第五到第十行。


你如何在(上下文)之前或之后也带出X文件,就像在grep -C中一样?这样可以方便地查找源代码中的错误。 - undefined

23

最佳表现方法

sed '5q;d' file

由于 sed 在第五行之后停止读取任何行。

更新自Roger Dueck先生的实验

我安装了 wcanadian-insane(6.6MB),并使用 time 命令比较了 sed -n 1p /usr/share/dict/words 和 sed '1q;d' /usr/share/dict/words 的性能;前者花费了 0.043 秒,而后者只需 0.002 秒,因此使用 'q' 明显提高了性能!


2
这通常也被写作:sed -n 5q - William Pursell
3
我喜欢这个解决方案,因为sed在第五行后停止读取任何行。 - Anthony Geoghegan
2
我安装了wcanadian-insane(6.6MB),并使用time命令比较了sed -n 1p /usr/share/dict/wordssed '1q;d' /usr/share/dict/words。第一个命令花费了0.043秒,而第二个命令只需要0.002秒,因此使用“q”肯定是一种性能提升! - Roger Dueck
2
警告:在管道中使用 q 命令会导致 broken pipe。在这种情况下,必须采用 sed -n 5p - stevesliva
@WilliamPursell sed -n 5q 对我来说什么也没打印出来。我认为答案给出的 sed -n '5{p;q}' 是最好的变体,但我也可以使用它。 - jmou

12
如果您想获取文件中第10到20行的内容,可以使用以下两种方法之一:
head -n 20 york.txt | tail -11

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

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

你会看到以下内容: 在此输入图片描述


2
你可以使用 sed 命令。
如果首选行号是 5:
sed -n '5p' filename #get the 5th line and prints the value (p stands for print)

如果首选行号是一个范围,例如1-5行:
sed -n '1,5p' filename #get the 1 to 5th line and prints the values

如果需要获取第1行和第5行,例如第1行、第5行:
sed -n '1p;5p;' filename #get the 1st and 5th line values only

2

处理这种任务的标准方法是使用外部工具。在编写shell脚本时禁止使用外部工具是荒谬的。但是,如果您真的不想使用外部工具,可以使用以下代码打印第5行:

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

请注意,这将打印逻辑行5。也就是说,如果input-file包含行继续符,则它们将被视为单个行。您可以通过在读取命令中添加-r来更改此行为。(这可能是期望的行为。)

1
"$((++i))" 似乎是 Bashism;如果 OP 受限于使用外部工具,我不会假设他们将能够访问除了纯粹的 "/bin/sh" 之外的更多内容。 - Josip Rodin
@JosipRodin 不,这是一个POSIX特性(但对于++增量的支持被明确标记为可选)。 - tripleee
@tripleee 这个方法在现代的dash作为/bin/sh时无法正常工作,所以我不会依赖它。 - Josip Rodin
但是像 $((i+=1)) 这样的简单解决方法在 Dash 中也可以使用。 - tripleee
$(($i+1))是我想到的简单解决方法。 - Josip Rodin

2

我并不特别喜欢任何一个答案。

以下是我的做法。

# 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
#!/bin/bash
for i in {1..50}
do
 line=$(sed "${i}q;d" file.txt)
 echo $line
done

1

假设这个问题是针对bash的,以下就是最快最简单的方式。

readarray -t a <file ; echo ${a[5-1]}

当不再需要数组a时,您可以将其丢弃。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接