3 votes

Comment itérer une chaîne de caractères avec des blancs ?

J'ai le code suivant :

line="95:p1=a b c 95:p2=d e 96:p1=a b c 96:p2=d e"

for l in $line; do
    echo $l
done

J'ai pris la suite :

95:p1=a
b
c
95:p2=d
e
96:p1=a
b
c
96:p2=d
e

Mais en fait a b c est une chaîne entière dans mon entreprise, donc si possible je pourrais obtenir la suite avec quelques moyens ?

95:p1=a b c
95:p2=d e
96:p1=a b c
96:p2=d e

3voto

RavinderSingh13 Points 29608

1ère solution : Avec les échantillons et les tentatives que vous avez montrés, veuillez essayer ce qui suit awk code. Écrit et testé avec GNU awk .

Voici le Démonstration en ligne pour les expressions rationnelles utilisées.

echo "$line"
95:p1=a b c 95:p2=d e 96:p1=a b c 96:p2=d e

awk -v RS='[0-9]{2}:p[0-9]=[a-zA-Z] [a-zA-Z]( [a-zA-Z]|$)*' 'RT{print RT}' <<<"$line"

Les résultats obtenus avec les échantillons présentés seront les suivants :

95:p1=a b c
95:p2=d e
96:p1=a b c
96:p2=d e


2ème solution : Avec n'importe quel système POSIX awk Veuillez essayer ce qui suit awk code :

awk '
{
  while(match($0,/[0-9]{2}:p[0-9]=[a-zA-Z] [a-zA-Z]( [a-zA-Z]|$)*/)){
    print substr($0,RSTART,RLENGTH)
    $0=substr($0,RSTART+RLENGTH)
  }
}
' <<<"$line"

2voto

chepner Points 54078

Il n'est pas possible de faire cela avec des paramètres ordinaires. Si vous souhaitez une collection de chaînes de caractères pouvant contenir des espaces, utilisez un tableau.

line=("95:p1=a b c" "95:p2=d e" "96:p1=a b c" "96:p2=d e")

for l in "${line[@]}"; do
    echo "$l"
done

Sinon, vous devrez faire la distinction entre les espaces "littéraux" et les espaces "délimiteurs". (Ce dernier est peut-être suivi de <num>: mais cette logique n'est pas triviale à mettre en œuvre à l'aide de bash les expressions régulières. Vous feriez probablement mieux d'utiliser un langage plus performant plutôt que d'essayer de faire cela en bash .)

2voto

glenn jackman Points 69748

Avec bash

read -ra f <<<"$line"    # split the string into words
n=${#f[@]}
i=0
lines=()
while ((i < n)); do
    l=${f[i++]}
    until ((i == n)) || [[ ${f[i]} =~ ^[0-9]+: ]]; do
        l+=" ${f[i++]}"
    done
    lines+=( "$l" )
done
declare -p lines

sorties

declare -a lines=([0]="95:p1=a b c" [1]="95:p2=d e" [2]="96:p1=a b c" [3]="96:p2=d e")

Maintenant, vous pouvez faire

for l in "${lines[@]}"; do
    do_something_with "$l"
done

Ou sédimentaire et capturer les lignes avec l'outil intégré de bash mapfile

mapfile -t lines < <(sed -E 's/ ([0-9]+:)/\n\1/g' <<< "$line")

2voto

echo "${line}" | 

mawk 'BEGIN { FS=RS="^$"(ORS="") } gsub(" [^ :]+:","\1&") + gsub("\1.","\n")^_'
95:p1=a b c
95:p2=d e
96:p1=a b c
96:p2=d e

2voto

tshiono Points 4286

Si votre grep soutiens -P (PCRE), veuillez essayer :

grep -Po "\d+:.*?(?=(?:\s*\d+:|$))" <<< "$line"

Sortie :

95:p1=a b c
95:p2=d e
96:p1=a b c
96:p2=d e

Explication de l'expression rationnelle \d+:.*?(?=(?:\s*\d+:|$)) :

  • \d+: correspond à des chiffres suivis de deux points. Il correspondra à 95: ou 96: .
  • .*?(?=pattern) correspond à la plus courte séquence de caractères suivie par le pattern . (?=pattern) est une assertion d'anticipation qui n'est pas incluse dans le résultat de l'analyse.
  • En pattern ci-dessus est décrite comme (?:\s*\d+:|$) , une alternance de digits followed by a colon ou end of the string . Le premier correspond à la partie initiale de l'élément suivant. L'élément \s* antes de \d+ correspond à un ou plusieurs caractères d'espacement, ce qui coupe le espace(s) dans le résultat de la recherche.

Si vous voulez itérer sur les sous-chaînes divisées, vous pouvez dire :

while IFS= read -r i; do
    echo "$i"   # or whatever you want to do with "$i"
done < <(grep -Po "\d+:.*?(?=(?:\s*\d+:|$))" <<< "$line")

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X