web-dev-qa-db-fra.com

Toujours inclure la première ligne dans grep

Je grep souvent des fichiers CSV avec des noms de colonne sur la première ligne. Par conséquent, je veux que la sortie de grep inclue toujours la première ligne (pour obtenir les noms des colonnes) ainsi que toutes les lignes correspondant au motif grep. Quelle est la meilleure façon de procéder?

36
jhourback

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:?filename is empty}"; }
38
kev

grep n'a pas vraiment de concept de numéro de ligne, contrairement à awk. Voici donc un exemple de lignes contenant "Incoming" - et la première ligne, quelle qu'elle soit:

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

Vous pourriez faire un script (un peu excessif, cependant). J'ai créé un fichier, grep + 1, et je l'ai mis dedans:

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

Maintenant on peut:

./grep+1 Incoming

edit: supprime le "{print;}", qui est l'action par défaut de awk.

9
Alex North-Keys

Vous pouvez utiliser sed au lieu de grep pour faire ceci:

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

Cela imprimera la première ligne deux fois, cependant, si elle contient le motif.

-n indique à sed de ne pas imprimer chaque ligne par défaut.
-e '1p' imprime la première ligne.
-e '/pattern/p' imprime chaque ligne correspondant au motif.

8
Adam Liss

C'est une solution très générale, par exemple si vous souhaitez trier un fichier tout en conservant la première ligne. En gros, "passe la première ligne telle quelle, puis fais ce que je veux (awk/grep/sort/what) sur le reste des données."

Essayez ceci dans un script, en l’appelant peut-être keepfirstline (n'oubliez pas chmod +x keepfirstline et mettez-le dans votre PATH):

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

Il peut être utilisé comme suit:

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

ou peut-être, si vous voulez filtrer avec awk

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

J'aime souvent trier un fichier, mais en gardant l'en-tête dans la première ligne

cat your.data.csv | keepfirstline sort

keepfirstline exécute la commande qui lui est donnée (grep SearchTerm), mais seulement après avoir lu et imprimé la première ligne.

3
Aaron McDaid

Une autre option:

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

Exemple:

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

Sortie:

title
value2
2
Eyal Levin

Vous pouvez inclure une correspondance de modèle différente pour l'un des noms de colonne. Si une colonne s'appelaitCOLalors ceci fonctionnerait:

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

Donc, j'ai posté une réponse courte complètement différente il y a quelque temps.

Cependant, pour ceux qui recherchent une commande qui ressemble à grep en termes de prise des mêmes options (bien que ce script vous oblige à utiliser les options longues si un optarg est impliqué), et qui peut gérer des caractères étranges dans les noms de fichiers, etc., etc. .. Amusez-vous en tirant cela à part.

C'est essentiellement un grep qui émet toujours la première ligne. Si vous pensez qu'un fichier sans lignes correspondantes devrait ignorer l'émission de cette première ligne (en-tête), eh bien, cela reste un exercice pour le lecteur. J'ai enregistré est en tant que 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
0
Alex North-Keys