web-dev-qa-db-fra.com

'+' (une ou plusieurs occurrences) ne fonctionne pas avec la commande 'sed'

J'essaie d'affiner mon code en supprimant les espaces blancs inutiles, les lignes vides et en équilibrant les parenthèses avec un espace entre elles, donc:

    int a = 4;
    if ((a==4) ||   (b==5))

    a++   ;

devrait changer en:

    int a = 4;
    if ( (a==4) || (b==5) )
    a++ ;

Cela fonctionne pour les crochets et les lignes vides. Cependant, il oublie de réduire les espaces multiples à un seul espace:

    int a = 4;
    if ( (a==4) ||   (b==5) )
    a++    ;

Voici mon script:

    #!/bin/bash
    # Script to refine code
    #
    filename=read.txt

    sed 's/((/( (/g' $filename > new.txt
    mv new.txt $filename

    sed 's/))/) )/g' $filename > new.txt
    mv new.txt $filename

    sed 's/ +/ /g' $filename > new.txt
    mv new.txt $filename

    sed '/^$/d' $filename > new.txt
    mv new.txt $filename

Existe-t-il également un moyen de rendre ce script plus concis, par ex. supprimer ou réduire le nombre de commandes?

56
Siddhartha

Si vous utilisez GNU sed alors vous devez utiliser sed -r qui force sed à utiliser des expressions régulières étendues, y compris le comportement souhaité de +. Voir man sed:

-r, --regexp-extended

       use extended regular expressions in the script.

Il en va de même si vous utilisez OS X sed, mais vous devez alors utiliser sed -E:

-E      Interpret regular expressions as extended (modern) regular expressions
        rather than basic regular regular expressions (BRE's).
65
Sicco

Vous devez précéder + avec un \, sinon sed essaie de faire correspondre le caractère + lui-même.

Pour rendre le script "plus intelligent", vous pouvez accumuler toutes les expressions dans un sed:

sed -e 's/((/( (/g' -e 's/))/) )/g' -e 's/ \+/ /g' -e '/^$/d' $filename > new.txt

Certaines implémentations de sed prennent même en charge le -i option qui permet de changer le fichier en place.

13
choroba

Parfois, -r et -e ne fonctionneront pas. J'utilise la version 4.2.1 de sed et ils ne fonctionnent pas du tout pour moi.

Un hack rapide consiste à utiliser l'opérateur * à la place. Disons donc que nous voulons remplacer tous les caractères d'espace redondants par un seul espace: Nous aimerions faire:

sed 's/ +/ /'

Mais nous pouvons utiliser ceci à la place:

sed 's/  */ /'

(notez le double espace)

9
statueofmike

Ce n'est peut-être pas la solution la plus propre. Mais si vous voulez éviter -E et -r pour rester compatible avec les deux versions de sed, vous pouvez répéter le caractère cc* - c'est 1 c puis 0 ou plus c's == 1 ou plusieurs c.

Ou utilisez simplement la syntaxe BRE, comme suggéré par @ cdarke , pour faire correspondre un nombre ou des modèles spécifiquesc\{1,\}. Le deuxième nombre après la virgule est exclu pour signifier 1 ou plus.

3
dosentmatter

sur le front bash;

J'ai d'abord fait un script test.sh

cat test.sh

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "Text read from file: $line"
    SRC=`echo $line | awk '{print $1}'`
    DEST=`echo $line | awk '{print $2}'`
    echo "moving $SRC to $DEST"
    mv $SRC $DEST || echo "move $SRC to $DEST failed" && exit 1
done < "$1"

puis on fait un fichier de données et un fichier de test aaa.txt

cat aaa.txt
<tag1>19</tag1>
<tag2>2</tag2>
<tag3>-12</tag3>
<tag4>37</tag4>
<tag5>-41</tag5>

puis testez et affichez les résultats.

bash test.sh list.txt 
Text read from file: aaa.txt bbb.txt
moving aaa.txt to bbb.txt
1
Calvin Taylor

Cela pourrait fonctionner pour vous:

sed -e '/^$/d' -e ':a' -e 's/\([()]\)\1/\1 \1/g' -e 'ta' -e 's/  */ /g' $filename >new.txt
1
potong