总是在 grep 中包含第一行

我经常用第一行的列名来抓取 CSV 文件。因此,我希望 grep 的输出始终包含第一行(以获取列名)以及与 grep 模式匹配的任何行。最好的方法是什么?

26578 次浏览

照做就是了

head -1 <filename>

然后执行 grep

您可以为其中一个列名包含备用模式匹配。如果一个列被称为 上校,那么这个方法就可以奏效:

$ grep -E 'COL|pattern' file.csv

您可以使用 sed而不是 grep来完成以下操作:

sed -n -e '1p' -e '/pattern/p' < $FILE

但是,如果第一行恰好包含模式,那么它将打印两次。

-n告诉 sed在默认情况下不要打印每一行。
-e '1p'打印第一行。
-e '/pattern/p'打印与模式匹配的每一行。

Grep 实际上没有行号的概念,但 awk 有,所以这里有一个输出行包含“ Incoming”的示例——以及第一行,不管它是什么:

awk 'NR == 1 || /Incoming/' foo.csv

你可以创建一个脚本(有点多余,但是) ,我创建了一个文件 grep + 1,并把它放进去:

#!/bin/sh
pattern="$1" ; shift
exec awk 'NR == 1 || /'"$pattern"'/' "$@"

现在人们可以:

./grep+1 Incoming

Edit: 删除“{ print; }”,这是 awk 的默认操作。

教育局局长:

sed '1p;/pattern/!d' input.txt

谢谢:

awk 'NR==1 || /pattern/' input.txt

Grep1:

grep1() { awk -v pattern="${1:?pattern is empty}" 'NR==1 || $0~pattern' "${2:-/dev/stdin}"; }

这是一个非常通用的解决方案,例如,如果您想对文件进行排序,同时保持第一行不变。基本上就是 “按原样传递第一行,然后对其余数据做我想做的(ABC0/ABC1/sort/whatever)。”

在脚本中尝试一下,可以把它命名为 keepfirstline(不要忘记 chmod +x keepfirstline,把它放在 PATH中) :

#!/bin/bash
IFS='' read -r JUST1LIINE
printf "%s\n" "$JUST1LIINE"
exec "$@"

它可用于以下方面:

cat your.data.csv | keepfirstline grep SearchTerm > results.with.header.csv

或者,如果你想用 awk过滤

cat your.data.csv | keepfirstline awk '$1 < 3' > results.with.header.csv

我通常喜欢对文件进行排序,但是把标题放在第一行

cat your.data.csv | keepfirstline sort

keepfirstline执行给定的命令(grep SearchTerm) ,但是只在读取和打印第一行之后执行。

另一个选择:

$ cat data.csv | (read line; echo "$line"; grep SEARCH_TERM)

例如:

$ echo "title\nvalue1\nvalue2\nvalue3" | (read line; echo "$line"; grep value2)

产出:

title
value2

所以,我发布了一个完全不同的简短回答上面一段时间回来。

然而,对于那些渴望使用所有相同选项(尽管这个脚本要求您使用长选项,如果涉及 optarg)的命令,并且可以处理文件名中奇怪的字符,等等。好好享受拆开它的过程吧。

本质上,它是一个总是发出第一行的 grep。如果您认为没有匹配行的文件应该跳过发出第一行(标题) ,那么,这将留给读者作为练习。我保存的是 grep+1

#!/bin/bash
# grep+1 [<option>...] [<regex>] [<file>...]
# Emits the first line of each input and ignores it otherwise.
# For grep options that have optargs, only the --forms will work here.


declare -a files options
regex_seen=false
regex=


double_dash_seen=false
for arg in "$@" ; do
is_file_or_rx=true
case "$arg" in
-*) is_file_or_rx=$double_dash_seen ;;
esac
if $is_file_or_rx ; then
if ! $regex_seen ; then
regex="$arg"
regex_seen=true
else
files[${#files[*]}]="$arg"     # append the value
fi
else
options[${#options[*]}]="$arg"     # append the value
fi
done


# We could either open files all at once in the shell and pass the handles into
# one grep call, but that would limit how many we can process to the fd limit.
# So instead, here's the simpler approach with a series of grep calls


if $regex_seen ; then
if [ ${#files[@]} -gt 0 ] ; then
for file in "${files[@]}" ; do
head -n 1 "$file"
tail -n +2 "$file" | grep --label="$file" "${options[@]}" "$regex"
done
else
grep "${options[@]}"   # stdin
fi
else
grep "${options[@]}"   # probably --help
fi


#--eof

所有的答案都是正确的。对于包含第一行的命令(而不是文件)的输出,可以这样做; -)

df -h | grep -E '(^Filesystem|/mnt)'  # <<< returns usage of devices, with mountpoint '/mnt/...'
ps aux | grep -E '(^USER|grep)'       # <<< returns all grep-process

Grep 的 -E选项启用了它的 regex 模式。我们 grep 的字符串使用 |,并且可以解释为“ or”,因此我们在 df中查找行:

  • Filesystem开头(第一个子表达式中的’^’表示“ line start with”)
  • 以及包含 /mnt的行

另一种方法是将输出通过管道传输到 tempfile中,并像其他文章中显示的那样对内容进行抓取。如果您不知道第一行的内容,这可能会很有帮助。

head -1 <file> && grep ff <file>