bash 如何从文件或脚本中的管道中选择多行?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27509898/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to select multiple lines from a file or from pipe in a script?
提问by Brad Parks
I'd like to have a script, called lines.sh
that I can pipe data to to select a series of lines.
我想要一个脚本,称为lines.sh
我可以通过管道传输数据以选择一系列行。
For example, if I had the following file:
例如,如果我有以下文件:
test.txt
测试.txt
a
b
c
d
Then I could run:
然后我可以运行:
cat test.txt | lines 2,4
and it would output
它会输出
b
d
I'm using zsh, but would prefer a bash solution if possible.
我正在使用 zsh,但如果可能的话,我更喜欢 bash 解决方案。
回答by anubhava
You can use this awk:
你可以使用这个awk:
awk -v s='2,4' 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' file
two
four
Via a separate script lines.sh
:
通过单独的脚本lines.sh
:
#!/bin/bash
awk -v s="" 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' ""
Then give execute permissions:
然后赋予执行权限:
chmod +x lines.sh
And call it as:
并将其称为:
./lines.sh '2,4' 'test.txt'
回答by pfnuesel
Try sed
:
尝试sed
:
sed -n '2p; 4p' inputFile
-n
tells sed
to suppress output, but for the lines 2
and 4
, the p
(print) command is used to print these lines.
-n
告诉sed
抑制输出,但对于行2
和4
,p
(print) 命令用于打印这些行。
You can also use ranges, e.g.:
您还可以使用范围,例如:
sed -n '2,4p' inputFile
回答by gniourf_gniourf
Two pure Bash versions. Since you're looking for general and reusable solutions, you might as well put a little bit of effort in that. (Also, see last section).
两个纯 Bash 版本。既然您正在寻找通用且可重用的解决方案,那么您不妨在这方面付出一些努力。(另请参阅最后一节)。
Version 1
版本 1
This script slurps the entire stdin into an array (using mapfile
, so it's rather efficient) and then prints the lines specified on its arguments. Ranges are valid, e.g.,
该脚本将整个 stdin 放入一个数组中(使用mapfile
,因此效率很高),然后打印在其参数中指定的行。范围是有效的,例如,
1-4 # for lines 1, 2, 3 and 4
3- # for everything from line 3 till the end of the file
You may separate these by spaces or commas. The lines are printed exactly in the order the arguments are given:
您可以用空格或逗号将它们分开。这些行完全按照给定参数的顺序打印:
lines 1 1,2,4,1-3,4- 1
will print line 1 twice, then line 2, then line 4, then lines 1, 2 and 3, then everything from line 4 till the end, and finally, line 1 again.
将打印第 1 行两次,然后是第 2 行,然后是第 4 行,然后是第 1、2 和 3 行,然后是从第 4 行到最后的所有内容,最后再次打印第 1 行。
Here you go:
干得好:
#!/bin/bash
lines=()
# Slurp stdin in array
mapfile -O1 -t lines
# Arguments:
IFS=', ' read -ra args <<< "$*"
for arg in "${args[@]}"; do
if [[ $arg = +([[:digit:]]) ]]; then
arg=$arg-$arg
fi
if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
((from=10#${BASH_REMATCH[1]}))
((to=10#${BASH_REMATCH[2]:-$((${#lines[@]}))}))
((from==0)) && from=1
((to>=${#lines[@]})) && to=${#lines[@]}
((from<=to)) || printf >&2 'Argument %d-%d: lines not in increasing order' "$from" "$to"
for((i=from;i<=to;++i)); do
printf '%s\n' "${lines[i]}"
done
else
printf >&2 "Error in argument \`%s'.\n" "$arg"
fi
done
- Pro: It's really cool.
- Con: Needs to read entire stream into memory. Not suitable for infinite streams.
- 亲:真的很酷。
- 缺点:需要将整个流读入内存。不适合无限流。
Version 2
版本 2
This version addresses the previous problem of infinite streams. But you'll lose the ability to repeat and reorder lines.
此版本解决了之前的无限流问题。但是您将失去重复和重新排序行的能力。
Same thing, ranges are allowed:
同样的事情,范围是允许的:
lines 1 1,4-6 9-
will print lines 1, 4, 5, 6, 9 and everything till the end. If the set of lines is bounded, exits as soon as last line is read.
将打印第 1、4、5、6、9 行以及所有内容直到最后。如果行集有界,则在读取最后一行后立即退出。
#!/bin/bash
lines=()
tillend=0
maxline=0
# Process arguments
IFS=', ' read -ra args <<< "$@"
for arg in "${args[@]}"; do
if [[ $arg = +([[:digit:]]) ]]; then
arg=$arg-$arg
fi
if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
((from=10#${BASH_REMATCH[1]}))
((from==0)) && from=1
((tillend && from>=tillend)) && continue
if [[ -z ${BASH_REMATCH[2]} ]]; then
tillend=$from
continue
fi
((to=10#${BASH_REMATCH[2]}))
if ((from>to)); then
printf >&2 "Invalid lines order: %s\n" "$arg"
exit 1
fi
((maxline<to)) && maxline=$to
for ((i=from;i<=to;++i)); do
lines[i]=1
done
else
printf >&2 "Invalid argument \`%s'\n" "$arg"
exit 1
fi
done
# If nothing to read, exit
((tillend==0 && ${#lines[@]}==0)) && exit
# Now read stdin
linenb=0
while IFS= read -r line; do
((++linenb))
((tillend==0 && maxline && linenb>maxline)) && exit
if [[ ${lines[linenb]} ]] || ((tillend && linenb>=tillend)); then
printf '%s\n' "$line"
fi
done
- Pro: It's really cool and doesn't read the full stream in memory.
- Con: Can't repeat or reorder lines as Version 1. Speed is not is it's strongest point.
- 优点:它真的很酷,并且不会读取内存中的完整流。
- 缺点:不能像版本 1 那样重复或重新排列行。速度不是它的最强点。
Further thoughts
进一步的想法
If you really want an awesome general script that does what Version 1 and Version 2 does, and more, you definitely should consider using another language, e.g., Perl: you'll gain a lot (in particular speed)! you'll be able to have nice options that'll do lots of much cooler stuff. It might be worth it in the long run, as you want a general and reusable script. You might even end up having a script that reads emails!
如果您真的想要一个很棒的通用脚本来执行版本 1 和版本 2 的功能,甚至更多,您绝对应该考虑使用另一种语言,例如 Perl:您将获得很多(尤其是速度)!你将能够有很好的选择,可以做很多更酷的事情。从长远来看,这可能是值得的,因为您需要一个通用且可重用的脚本。您甚至可能最终拥有一个阅读电子邮件的脚本!
Disclaimer.I haven't thoroughly checked these scripts... so beware of bugs!
免责声明。我还没有彻底检查这些脚本......所以要小心错误!
回答by midori
Quick solution for you friend. Input:
您朋友的快速解决方案。输入:
test.txt
测试.txt
a
b
c
d
e
f
g
h
i
j
test.sh
测试文件
lines (){
sed -n "$( echo "$@" | sed 's/[0-9]\+/&p;/g')"
}
cat 1.txt | lines 1 5 10
Or if you want to have your lines
as script:
或者,如果您想使用lines
as 脚本:
lines.sh
行.sh
IFS=',' read -a lines <<< ""; sed -n "$( echo "${lines[@]}" | sed 's/[0-9]\+/&p;/g')" ""
./lines.sh 1,5,10 test.txt
Output in both cases:
两种情况下的输出:
a
e
j
回答by ChatterOne
Well, provided that:
好吧,前提是:
- your file is small enough
- you don't have any semicolon (or another specific character of your choice) in the file
- you don't mind using multiple pipes
- 你的文件足够小
- 您在文件中没有任何分号(或您选择的其他特定字符)
- 你不介意使用多个管道
you could use something like:
你可以使用类似的东西:
cat test.txt |tr "\n" ";"|cut -d';' -f2,4|tr ";" "\n"
Where -f2,4 indicates the lines you want to extract
其中 -f2,4 表示要提取的行
回答by Bernardo Rufino
回答by buydadip
Try this :
尝试这个 :
file=
for var in "$@" //var is all line numbers
do
sed -n "${var}p" $file
done
I created a script with 1 file parameter, and an unlimited number of parameters for line numbers. You would call it as so :
我创建了一个带有 1 个文件参数和无限数量的行号参数的脚本。你会这样称呼它:
lines txt 2 3 4...etc