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.
5 Réponses :
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 / .
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'
.
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....."
où "....."
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.
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
Bonne utilisation de pcregrep, ce sont d'excellentes solutions merci pour le partage, bravo.
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
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.
Je dois faire correspondre
x
et imprimerx
plus les deux champs suivants:Utiliser
awk
sans aucune regex:x 123 456 x 678 910sed -E 's/(^|.*\t)(x(\t[^\t]+){2}).*/\2/' fileOu, en utilisant
gnu sed
:x 123 456 x 678 910awk 'BEGIN {FS=OFS="\t"} {for (i=1; i<=NF; ++i) if ($i == "x") break; print $i, $(i+1), $(i+2)}' file