2010-09-23 26 views
2

J'ai écrit une commande shell canalisée dans laquelle plusieurs canalisations fonctionnent. Je veux maintenant mettre cela sous la forme d'un script (tidy) shell. Voici le script:Création d'un script shell ordonné à partir d'un pipeline de ligne de commande moche

#!/bin/bash 
for number in `cat xmlEventLog_2010-03-23T* | sed -nr "/<event eventTimestamp/,/<\/event>/ {/event /{s/^.*$/\n/; p};/payloadType/{h; /protocol/ {s/.*protocol=\"([^\"]*)?\".*/protocol: \1/}; p; x; /type/ {s/.*type=\"([^\"]+)\".*/payload: \1/g}; /type/! {s/.*protocol=\"([^\"]+)\".*/payload: \1/g}; p};/sender/{/sccpAddress/ {s/.*sccpAddress=\"([^\"]*)?\".*/sccpAddress: \1/}; /sccpAddress/! {s/.*/sccpAddress: Unknown/}; p};/result /{s/.*value=\"([^\"]+)\".*/result: \1/g; p};/filter code/{s/.*type=\"([^\"]+)\".*/type: \1/g; p};}"| tee checkThis.txt| awk 'BEGIN{FS="\n"; RS=""; OFS=";"; ORS="\n"} $1~/result: Blocked|Modified/ && $2~/sccpAddress: 353201000001/ && $4~/payload: SMS-MO-FSM-INFO|SMS-MO-FSM/ {$1=$1 ""; print}' | sort | uniq -c| egrep "NUMBER_BLACKLIST|USER_BLACKLIST|NUMBER_WALLEDGARDEN|USER_WALLED_GARDEN|SERVICE_RESTRICTION|BLOCK_VOICE_TO_SMS|PEP_Blacklist_Whitelist" | awk '{print $1}'`; do fil="$fil+$number" 
done 
echo "fil is $fil" 

Je voudrais ranger cela afin qu'il soit lisible. La boucle for qui conduit dans Sed et awk est cochon laid à voir. Est-ce que quelqu'un a eu des suggestions pour ranger cette monstruosité piped. Est-ce que les tuyaux m'empêcheraient de casser ça en différentes lignes?

Merci

A

Si vous copiez les lignes ci-dessus pour agenda, vous verrez ce que je veux dire au sujet laid (mais fonctionnel)

gens Ok. Voici la version finale nettoyée.

Il a été mentionné que la fonction event_structure pouvait être entièrement effectuée dans awk. Je me demande si quelqu'un pourrait me montrer un exemple de la façon dont cela pourrait être fait. Le séparateur d'enregistrements serait défini sur/event et cela séparerait les événements, mais ce sont les structures qui se trouvent dans events.txt (voir ci-dessous) qui m'intéressent. Le résultat numérique est sans importance.

Le cœur du code se trouve dans la fonction event_structure. Je veux analyser les données et tout mettre dans des structures de données pour une inspection plus tard, le cas échéant. Ce qui suit fonctionne bien. Sur la ligne qui commence par payloadType, j'ai besoin d'analyser 2 valeurs ou de définir des valeurs manquantes à Unknown. Est-ce totalement inatteignable ou est-ce que la combinaison sed/awk que j'ai ici est la meilleure façon de le faire?

#!/bin/bash 

event_structure() { 
     sed -nr "/<event eventTimestamp/,/<\/event>/ { 
      /event /{s/^.*$/\n/; p} 
      /payloadType/{h; /protocol/ {s/.*protocol=\"([^\"]*)?\".*/protocol: \1/}; p; x; /type/ {s/.*type=\"([^\"]+)\".*/payload: \1/g}; /type/! {s/.*protocol=\"([^\"]+)\".*/payload: \1/g}; p} 
      /sender/{/sccpAddress/ {s/.*sccpAddress=\"([^\"]*)?\".*/sccpAddress: \1/}; /sccpAddress/! {s/.*/sccpAddress: Unknown/}; p} 
      /result /{s/.*value=\"([^\"]+)\".*/result: \1/g; p} 
      /filter code/{s/.*type=\"([^\"]+)\".*/type: \1/g; p};}" xmlEventLog_2010-03-23T* | 
     tee events.txt| 
     awk 'BEGIN{FS="\n"; RS=""; OFS=";"; ORS="\n"} 
     $1~/result: Blocked|Modified/ && $2~/sccpAddress: 353201000001/ && $4~/payload: SMS-MO-FSM-INFO|SMS-MO-FSM/ {$1=$1 ""; print}' 
} 

numbers=$(event_structure | sort | uniq -c | egrep "NUMBER_BLACKLIST|USER_BLACKLIST|NUMBER_WALLEDGARDEN|USER_WALLED_GARDEN|SERVICE_RESTRICTION|BLOCK_VOICE_TO_SMS|PEP_Blacklist_Whitelist" | awk '{print $1}') 
addition=`echo $numbers | tr -s ' \n\t' '+' | sed -e '1s/^/fil is /' -e '$s/+$//'` 
for number in $numbers 
do 
     fil="$fil+$number" 
done 
echo $addition=$(($fil)) 

Voici une section du fichier events.txt produit:

result: Blocked 
sccpAddress: 353869000000 
protocol: SMS 
payload: COPS 
type: SERVICE_BLACK_LIST 
result: Blocked 


result: Blocked 
sccpAddress: 353869000000 
protocol: SMS 
payload: COPS 
type: SERVICE_BLACK_LIST 
result: Blocked 

result: Modified 
sccpAddress: Unknown 
protocol: IM 
payload: IM 
type: NUMBER_BLACKLIST 
result: Modified 

result: Allowed 
sccpAddress: Unknown 
protocol: MM1 
payload: MM1 

Voici la sortie:

$ ./bashShell.sh 
fil is 2+372+1+1+214+73+1+20=684 

Voici une sortie de seulement l'appel de fonction:

$ ./bashShell.sh | head -10 
result: Blocked;sccpAddress: 353201000001;protocol: SMS;payload: SMS-MO-FSM;type: TEXT_ANALYSIS;result: Blocked 
result: Blocked;sccpAddress: 353201000002;protocol: SMS;payload: SMS-MT-FSM;type: TEXT_ANALYSIS;result: Blocked 
result: Blocked;sccpAddress: 353201000005;protocol: SMS;payload: SMS-MO-FSM;type: SERVICE_BLACKLIST;result: Blocked 
result: Blocked;sccpAddress: 353201000021;protocol: SMS;payload: SMS-MT-FSM;type: NUMBER_BLACKLIST;result: Blocked 
result: Blocked;sccpAddress: 353201000033;protocol: IM;payload: IM;type: NUMBER_BLACKLIST;result: Blocked 
result: Blocked;sccpAddress: 353401009001;protocol: SMS;payload: SMS-MO-FSM;type: NUMBER_BLACKLIST;result: Blocked 
result: Blocked;sccpAddress: 353201000001;protocol: SMS;payload: SMS-MO-FSM;type: NUMBER_BLACKLIST;result: Blocked 
result: Blocked;sccpAddress: 353201000005;protocol: SMS;payload: SMS-MO-FSM;type: NUMBER_BLACKLIST;result: Blocked 
result: Blocked;sccpAddress: 353401000001;protocol: SMS;payload: SMS-MO-FSM;type: NUMBER_BLACKLIST;result: Blocked 
result: Blocked;sccpAddress: 353201000001;protocol: SMS;payload: SMS-MO-FSM;type: NUMBER_BLACKLIST;result: Blocked 

ps J'ai nommé le script ba shShell.sh sans raison particulière

A

Répondre

3

Pipes ne vous arrêtent pas lors de la rupture à plusieurs lignes, mais utiliser $(...) au lieu de guillemets obliques. Quelque chose comme cela devrait fonctionner:

#!/bin/bash 

for number in $(
    cat xmlEventLog_2010-03-23T* | 
    sed -nr "/<event eventTimestamp/,/<\/event>/ {/event /{s/^.*$/\n/; p};/payloadType/{h; /protocol/ {s/.*protocol=\"([^\"]*)?\".*/protocol: \1/}; p; x; /type/ {s/.*type=\"([^\"]+)\".*/payload: \1/g}; /type/! {s/.*protocol=\"([^\"]+)\".*/payload: \1/g}; p};/sender/{/sccpAddress/ {s/.*sccpAddress=\"([^\"]*)?\".*/sccpAddress: \1/}; /sccpAddress/! {s/.*/sccpAddress: Unknown/}; p};/result /{s/.*value=\"([^\"]+)\".*/result: \1/g; p};/filter code/{s/.*type=\"([^\"]+)\".*/type: \1/g; p};}"| 
    tee checkThis.txt | 
    awk 'BEGIN{FS="\n"; RS=""; OFS=";"; ORS="\n"} $1~/result: Blocked|Modified/ && $2~/sccpAddress: 353201000001/ && $4~/payload: SMS-MO-FSM-INFO|SMS-MO-FSM/ {$1=$1 ""; print}' | 
    sort | 
    uniq -c | 
    egrep "NUMBER_BLACKLIST|USER_BLACKLIST|NUMBER_WALLEDGARDEN|USER_WALLED_GARDEN|SERVICE_RESTRICTION|BLOCK_VOICE_TO_SMS|PEP_Blacklist_Whitelist" | 
    awk '{print $1}' 
); do fil="$fil+$number" 
done 
echo "fil is $fil" 

Bien sûr, la plus grande partie est de diviser le awk et sed skripts en plusieurs lignes aussi ...

Mais je crois que, même après que le résultat sera encore assez illisible.

Je suggérerais simplement de réécrire complètement le script en Perl, Ruby ou tout autre langage de script plus lisible que Bash. Ceci est juste une suggestion de mon expérience personnelle - chaque fois que je commence avec un script shell, je le réécris finalement dans Ruby. J'adore Bash, mais ça ne semble pas évoluer.

+0

Je l'ai écrit en python aussi (très joli) mais j'aime bien que la version de bash soit nettoyée. Merci – amadain

+0

Quel est le problème avec les backticks? –

+0

Je suis avec René. Vous devez utiliser quelque chose qui est adapté pour travailler avec des fichiers XML. Vous ne devriez pas essayer d'utiliser des regex pour cela. En deçà, la partie sed pourrait être réécrite en AWK, la 'egrep' et la' uniq' pourraient être faites en AWK. Si vous avez 'gawk', vous pouvez aussi faire le' sort'. Mais à la fin, vous devriez utiliser un module XML Python ou Perl. –

2

Deux petites remarques:

Mettre la « pour la liste » dans une fonction distincte:

number_list() { 
    # complete pipe command list 
    # divided over multiple lines 
} 

for number in `number_list` 
do 
    # ... 
done 

Essayez de combiner quelques-unes des commandes: Le cat n'est pas nécessaire, le egrep et awk final peut être combiné.

+0

Pourquoi le chat n'est-il pas nécessaire? – amadain

+1

@amadain: 'sed' prend aussi plusieurs arguments de fichier, donc vous pouvez remplacer le fichier cat ... | sed [pat] 'avec le fichier' sed [pat] ... '. – schot

1

Vous pouvez rejoindre les différents jetons en utilisant tr et précédez 'fil est' en utilisant sed:

pipeline | tr -s ' \n\t' '+' | sed -e '1s/^/fil is /' -e '$s/+$//' 

Le pipeline peut être répartie sur plusieurs lignes en utilisant:

first-command \ 
    | second-command \ 
    | third-command \ 
    ... 
    | last-command 
1

Le script shell est en fait la partie simple. Le script sed est le bit effrayant. Le script peut être amélioré avec des documents ici, mais soyez témoin du commentaire:

#!/bin/bash 

seds=/tmp/seds.$$ 
awks=/tmp/awks.$$ 
gres=/tmp/gres.$$ 

trap "rm -f $seds $awks $gres" 0 1 2 3 15 

# this is a noble and hairy attempt to parse xml with sed 
# it is extremely fragile and strongly dependent upon 
# the form of the source file never changing 
# I'm alternately proud or disgusted that I've been able 
# to get away with this 

cat > $seds <<'EOF' 
/<event eventTimestamp/,/<\/event>/ {/event /{s/^.*$/\n/; p}; 
/payloadType/{h; /protocol/ {s/.*protocol=\"([^\"]*)?\".*/protocol: \1/}; p; x; 
/type/ {s/.*type=\"([^\"]+)\".*/payload: \1/g}; 
/type/! {s/.*protocol=\"([^\"]+)\".*/payload: \1/g}; p}; 
/sender/{/sccpAddress/ {s/.*sccpAddress=\"([^\"]*)?\".*/sccpAddress: \1/}; 
/sccpAddress/! {s/.*/sccpAddress: Unknown/}; p}; 
/result /{s/.*value=\"([^\"]+)\".*/result: \1/g; p}; 
/filter code/{s/.*type=\"([^\"]+)\".*/type: \1/g; p};} 
EOF 

cat > $awks <<'EOF' 
BEGIN {FS="\n"; RS=""; OFS=";"; ORS="\n"} 
$1~/result: Blocked|Modified/ && \ 
$2~/sccpAddress: 353201000001/ && \ 
$4~/payload: SMS-MO-FSM-INFO|SMS-MO-FSM/ {$1=$1 ""; print} 
EOF 

cat > $gres <<EOF 
NUMBER_BLACKLIST 
USER_BLACKLIST 
NUMBER_WALLEDGARDEN 
USER_WALLED_GARDEN 
SERVICE_RESTRICTION 
BLOCK_VOICE_TO_SMS 
PEP_Blacklist_Whitelist 
EOF 

cat xmlEventLog_2010-03-23T* | \ 
sed -nr -f $seds | \ 
tee checkThis.txt | \ 
awk -f $awks | \ 
sort | uniq -c | \ 
fgrep -f $gres | \ 
awk '{print $1}'