始终在 grep 中包含第一行



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

sed:

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

awk:

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

grep1:

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

您可以为其中一个列名包含备用模式匹配项。如果一列称为 COL,那么这将起作用:

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

grep 并没有真正的行号概念,但 awk 有,所以这里有一个输出行包含"传入"的示例 - 以及第一行,不管它是什么:

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

你可以制作一个脚本(有点过分,但是(。 我做了一个文件,grep+1,把它放进去:

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

现在可以:

./grep+1 Incoming

编辑:删除了"{打印;}",这是AWK的默认操作。

您可以使用

sed而不是grep来执行此操作:

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

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

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

另一种选择:

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

例:

$ echo "titlenvalue1nvalue2nvalue3" | (read line; echo "$line"; grep value2)

输出:

title
value2

这是一个非常通用的解决方案,例如,如果要在保留第一行的同时对文件进行排序。基本上,"按原样传递第一行,然后在其余数据上做任何我想做的事情(awk/grep/sort/随便什么(。

在脚本中尝试一下,也许将其称为keepfirstline(不要忘记chmod +x keepfirstline并将其放在您的PATH中(:

#!/bin/bash
IFS='' read -r JUST1LIINE
printf "%sn" "$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(,但只有在读取并打印第一行之后。

只是做

head -1 <filename> 

然后执行grep

所以,不久前我在上面发布了一个完全不同的简短答案。

但是,对于那些渴望在采用所有相同选项方面看起来像 grep 的命令(尽管如果涉及 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选项启用其正则表达式模式。我们 grep 使用的字符串|并且可以解释为"or",所以我们在 df -exmaple 中查找行:

  • Filesystem 开头(第一个子表达式中的前导"^"表示"行开头为"(
  • 和行,其中包含/mnt

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

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

最新更新