1
votes

Rechercher une correspondance dans un champ et imprimer les n champs suivants

BASH noob ici. J'ai un fichier séparé par des tabulations structuré comme ceci:

x 123 456

x 678 910

Je dois faire correspondre "x" et imprimer x plus les deux champs suivants:

ABC DEF x 123 456

GHI x 678 910

J'ai essayé plusieurs choses mais le problème qui me dérange est que "x" n'est pas toujours dans le même domaine. Quelqu'un peut-il s'il vous plaît aider? Merci d'avance.


0 commentaires

5 Réponses :


0
votes

Si vous souhaitez tout supprimer avant le "x", vous pouvez exécuter une commande sed comme celle-ci:

sed 's/^.*x/x/g' file.txt

Il trouve toutes les occurrences du motif ^.*x et le remplace par x .

Répartition de ^.*x :
^ signifie le début d'une ligne
.* un motif générique qui peut être composé de plusieurs caractères
x le caractère "x"

Par conséquent, il remplace tout ce qui précède et y compris "x" qui sont sur la même ligne avec le nouveau motif, juste "x".

Pour plus d'informations sur la commande find and replace de sed, voir https://www.cyberciti.biz/faq/how-to-use-sed-to-find-and-replace-text-in-files-in-linux-unix -shell / .


1 commentaires

sed est un excellent choix. L'option g (globale) de s n'est pas vraiment nécessaire dans ce cas car la correspondance regex sera une correspondance «gourmande» du début au dernier 'x' .



1
votes

Si vous travaillez dans bash, alors bash fournit des extensions de paramètres avec suppression de sous-chaînes intégrées. Ils (avec beaucoup d'autres) sont:

$ grep -o 'x.*$' << 'eof'
> ABC DEF x 123 456
> GHI x 678 910
> eof
x 123 456
x 678 910

Donc, dans votre cas, vous voulez couper le plus long chemin de l'avant vers x comme motif, par exemple

grep -o 'x.*$' file

Où vous lisez chaque ligne, puis coupez à partir de l'avant jusqu'à ce que 'x' soit trouvé (vous supprimez également le 'x' ), donc vous sortez simplement "x.....""....." est reste de la ligne (restauration du 'x' )

(pour les grands ensembles de données, vous voudrez utiliser awk ou sed pour des raisons d'efficacité)

Exemple d'utilisation / sortie

En utilisant vos exemples de données dans un heredoc , vous pouvez faire:

while read line || [ -n "$line" ]; do
    echo "x${line##*x}"
done << 'eof'
ABC DEF x 123 456
GHI x 678 910
eof

Vous pouvez simplement sélectionner-copier / coller au milieu de la souris ce qui suit dans votre xterm pour tester:

$ while read line || [ -n "$line" ]; do
>     echo "x${line##*x}"
> done << 'eof'
> ABC DEF x 123 456
> GHI x 678 910
> eof
x 123 456
x 678 910

Utilisation de grep -o pour plus de simplicité

L'autre option consiste à utiliser grep -o où l'option -o renvoie la partie de la ligne correspondant uniquement à l'expression que vous fournissez, donc

while read line || [ -n "$line" ]; do
    echo "x${line##*x}"
done

Est une autre option simple, par exemple

${var#pattern}      Strip shortest match of pattern from front of $var
${var##pattern}     Strip longest match of pattern from front of $var
${var%pattern}      Strip shortest match of pattern from back of $var
${var%%pattern}     Strip longest match of pattern from back of $var

Dis moi si tu as d'autres questions.


0 commentaires

1
votes

Au cas où vous auriez besoin de faire correspondre uniquement le champ séparé par des tabulations x :

awk 'n=match($0,/(^|\t)x\t[^\t]*\t[^\t]*/) {$0=substr($0,n,RLENGTH); sub(/^\t/,""); print}' file
pcregrep -o '(^|\t)\Kx(\t[^\t]*){2}' file

Pour imprimer uniquement les deux champs suivants:

awk 'n=match($0,/(^|\t)x(\t|$)/) {$0=substr($0,n); sub(/^\t/,""); print}' file
pcregrep -o '(^|\t)\Kx(\t|$).*' file


1 commentaires

Bonne utilisation de pcregrep, ce sont d'excellentes solutions merci pour le partage, bravo.



1
votes

Pourriez-vous s'il vous plaît essayer de suivre, écrit et testé avec les exemples présentés dans GNU awk .

awk '                              ##Starting awk program from here.
match($0,/[[:space:]]+x[[:space:]]+[0-9]+[[:space:]]+[0-9]+$/){  ##Using match function to match regex here.
  val=substr($0,RSTART,RLENGTH)    ##Creating val which has sub string of matched regex(previous step) length.
  sub(/^[[:space:]]+/,"",val)      ##Substituting initial space with NULL in val here.
  print val                        ##Printing val here.
}
' Input_file                       ##mentioning Input_file name here.

OU pour faire correspondre plus d'un ensemble de chiffres après x avec des espaces, essayez de suivre.

awk '
match($0,/[[:space:]]+x[[:space:]]+([0-9]+[[:space:]]+){1,}[0-9]+/){
  val=substr($0,RSTART,RLENGTH)
  sub(/^[[:space:]]+/,"",val)
  print val
}
'  Input_file

Explication: Ajout d'une explication détaillée ci-dessus.

awk '
match($0,/[[:space:]]+x[[:space:]]+[0-9]+[[:space:]]+[0-9]+$/){
  val=substr($0,RSTART,RLENGTH)
  sub(/^[[:space:]]+/,"",val)
  print val
}
' Input_file


3 commentaires

Hehe ... J'ai pensé à awk pour celui-ci ... Mais j'ai dû te laisser quelque chose :)


Le seul autre auquel je pouvais penser était de boucler i<=NF et de vérifier si $i == "x" , puis de sortir de là jusqu'à la fin - mais c'était compliqué ...


@ DavidC.Rankin, oui pourquoi parce qu'il ne vérifiera pas le truc des chiffres, vous êtes ici.



1
votes

Je dois faire correspondre x et imprimer x plus les deux champs suivants:

Utiliser awk sans aucune regex:

x   123 456
x   678 910
sed -E 's/(^|.*\t)(x(\t[^\t]+){2}).*/\2/' file

Ou, en utilisant gnu sed :

x   123 456
x   678 910
awk 'BEGIN {FS=OFS="\t"} {for (i=1; i<=NF; ++i) if ($i == "x") break; 
print $i, $(i+1), $(i+2)}' file

0 commentaires