我想要一个脚本,名为lines.sh
,我可以通过pipe道数据来select一系列的行。
例如,如果我有以下文件:
的test.txt
ab c d
然后我可以运行:
cat test.txt | lines 2,4
它会输出
b d
我使用的是zsh,但如果可能的话,更喜欢bash解决scheme。
你可以使用这个awk:
awk -vs='2,4' 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' file two four
通过一个单独的脚本lines.sh
:
#!/bin/bash awk -vs="$1" 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' "$2"
然后给予执行权限:
chmod +x lines.sh
并称之为:
./lines.sh '2,4' 'test.txt'
试试sed
:
sed -n '2p; 4p' inputFile
-n
告诉sed
抑制输出,但对于第2
行和第4
,使用p
(打印)命令来打印这些行。
你也可以使用范围,例如:
sed -n '2,4p' inputFile
两个纯粹的Bash版本。 既然你正在寻找一般的和可重用的解决方案,那么你不妨在这方面投入一点努力。 (另请参阅上一节)。
这个脚本将整个stdin写入一个数组(使用mapfile
,所以效率很高),然后打印在其参数上指定的行。 范围是有效的,例如,
1-4 # for lines 1, 2, 3 and 4 3- # for everything from line 3 till the end of the file
你可以用空格或逗号分隔这些。 这些行按照参数的顺序完全打印:
lines 1 1,2,4,1-3,4- 1
将打印第一行两次,然后是第二行,然后是第四行,然后是第一行,第二行和第三行,然后是第四行到最后一行,最后是第一行。
干得好:
#!/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
这个版本解决了以前无限流的问题。 但是你将失去重复和重新排序的能力。
同样的事情,范围是允许的:
lines 1 1,4-6 9-
将打印行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
如果你真的想要一个真正的版本1和版本2的通用脚本,你肯定应该考虑使用另一种语言,例如Perl:你会获得很多(特别是速度)! 你将能够有很好的选择,会做很多更酷的东西。 从长远来看,这可能是值得的,因为你需要一个通用的,可重用的脚本。 你甚至可能最终有一个脚本,读取电子邮件!
免责声明。 我没有彻底检查这些脚本…所以要小心错误!
快速解决你的朋友。 输入:
的test.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
或者如果你想把你的lines
作为脚本:
lines.sh
IFS=',' read -a lines <<< "$1"; sed -n "$( echo "${lines[@]}" | sed 's/[0-9]\+/&p;/g')" "$2" ./lines.sh 1,5,10 test.txt
在这两种情况下输出:
a e j
如果这是一次性操作,并且没有多少行可以选择,可以使用pick来手动选择它们:
cat test.txt | pick | ...
交互式屏幕将打开,让您选择你想要的。
那么,只要:
你可以使用像这样的东西:
cat test.txt |tr "\\n" ";"|cut -d';' -f2,4|tr ";" "\\n"
其中-f2,4表示要提取的行
尝试这个 :
file=$1 for var in "$@" //var is all line numbers do sed -n "${var}p" $file done
我创建了一个具有1个文件参数的脚本,以及用于行号的无限数量的参数。 你会这样称呼它:
lines txt 2 3 4...etc