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?
sed '1p;/pattern/!d' input.txt
awk 'NR==1 || /pattern/' input.txt
grep1() { awk -v pattern="${1:?pattern is empty}" 'NR==1 || $0~pattern' "${2:?filename is empty}"; }
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.
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.
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.
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
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
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