Script correspondant au model littéral sur plusieurs lignes?

J'ai une string multiligne dans la variable $PAT . $PAT doit être recherché dans le file $FILE . Si $PAT est dans $FILE , il doit imprimer le file avec $PAT supprimé. Si $PAT n'est pas trouvé, n'imprimez rien. On ne sait pas si $PAT contient des caractères spéciaux, et il doit correspondre littéralement. Par exemple, si $PAT est //\/\\|* alors la même string de 8 caractères doit être recherchée dans $FILE .

Le monde réel utilise pour cela l'installation et la suppression de text dans des files / scripts déjà existants. Si vous voulez append $PAT dans $FILE , vous voulez savoir s'il a déjà été ajouté précédemment. Si $PAT est déjà dans $FILE , alors la sortie sans $PAT vous permet de le désinstaller facilement.

Les systèmes dont j'ai besoin d'un tel script pour les appareils Android ne disposent que de BusyBox. Pas de Perl ou d'autres langages de script.

Je suppose que vous réécrivez un file text qui tient dans la memory (on dirait que vous réécrivez un file de configuration).

Le script suivant utilise uniquement les fonctionnalités shell embeddedes et cat . Il devrait fonctionner sur la coquille d'Android, du less depuis Gingerbread et certainement depuis Ice Cream Sandwich. Il imprime le contenu du file less la première occurrence de $PAT s'il y en a un; si $PAT ne se produit pas, il n'imprime rien.

 contents=$(cat "$FILE") case $contents in *"$PAT"*) echo "${contents%%$PAT*}${contents#*$PAT}";; esac 

Cet extrait de code suppose que le file ne contient aucun octet nul, se termine par une seule nouvelle ligne et ne commence pas par un tiret. En outre, si le motif se termine par une nouvelle ligne, il ne sera pas trouvé à la fin du file. L'extrait de code plus complexe suivant gère les files text arbitraires:

 contents=$(cat "$FILE"; echo a) contents=${contents%a} case $contents in *"$PAT"*) contents="${contents%%$PAT*}${contents#*$PAT}" dashes=${contents%%[!-]*} echo -n "$dashes" echo -n "${contents#$dashes}";; esac 

(Notez que votre comportement proposé rend impossible de distinguer un file contenant exactement le model et un file vide.)

Il est en fait plus facile de mettre en œuvre votre script append / remove directement que d'utiliser la fonction intermédiaire proposée.

 contents=$(cat "$FILE"; echo a) contents=${contents%a} append= case $contents in *"$PAT"*) contents="${contents%%$PAT*}${contents#*$PAT}";; *) contents="$contents$PAT" esac dashes=${contents%%[!-]*} { echo -n "$dashes"; echo -n "${contents#$dashes}"; } >"$FILE.new" mv -- "$FILE.new" "$FILE" 

Si vous voulez faire correspondre $PAT tant que lignes complètes, j'ai une solution. Par lignes complètes, je veux dire que, dans le cas d'une correspondance, vous pouvez split $FILE en trois sous-files (f1, f2 & f3) où:

  • cat f1 f2 f3 est $FILE ,
  • f2 est $PAT .

Notez que f1 et / ou f3 peuvent être vides.

Tout d'abord, créez le file f2:

 cat << EOF > f2 $PAT EOF 

Ensuite, diff $ FILE et f2, en enregistrant le résultat:

 diff $FILE f2 > diff_res res=$? 

Si $res vaut zéro, alors f1 et f3 sont vides, et $ FILE est égal à $ PAT. Je suppose que vous voulez un file vide dans ce cas.

Si diff_res contient une ligne commençant par " > ", f2 contient au less une ligne qui ne diff_res pas dans $ FILE. Pour tester cela:

 grep -q '^> ' diff_res test $? -eq 0 && echo "PAT not found" 

Si diff_res ne contient pas de lignes commençant par " > ", toutes les lignes de f2 sont dans $ FILE, mais peut-être pas contiguës. S'il est contigu, diff_res contiendra soit:

  • Une seule ligne ne commence pas par " < " (si f1 ou f3 sont vides),
  • Deux lignes ne commencent pas par " < ", le 1er commençant toujours par " 1d " ou "1".

Pour tester ceci, nous avons:

 nb=$(grep -v "^< " diff_res | wc -l) if test $nb -gt 2; then pat_found=0 elif test $nb -eq 1; then pat_found=1 else pat_found=$(sed -n -e '1{/^1d/p;/^1,/p}' diff_res | wc -l) fi 

Ensuite, si pat_found vaut 1, le file sans $ PAT est le résultat diff avec seulement les lignes commençant par " < " sans ces 2 char:

 grep '^< ' diff_res | cut -c 3- 

Le script complet et réorganisé ressemblerait à ceci:

 # Output the desired result on stdin. f2=/tmp/f2 # Use of PID or mktmp would be better' diff_res=/tmp/diff_res # Use of PID or mktmp would be better' cat << EOF > $f2 $PAT EOF diff $FILE $f2 > $diff_res if test $? -ne 0; then grep -q '^> ' $diff_res if test $? -ne 0; then nb=$(grep -v "^< " $diff_res | wc -l) if test $nb -eq 1; then grep '^< ' $diff_res | cut -c 3- elif test $nb -eq 2; then pat_found=$(sed -n -e '1{/^1d/p;/^1,/p}' $diff_res | wc -l) test $pat_found -eq 1 && grep '^< ' $diff_res | cut -c 3- fi fi fi rm -f $f2 $diff_res 

Lire le file caractère par caractère. Si le caractère correspond au premier caractère de la variable, comparez le suivant, et ainsi de suite. Si la variable entière n'est pas appariée, revenez en arrière. Vous pouvez même implémenter un algorithm plus avancé pour le faire fonctionner plus rapidement, mais comme votre langage se trouve être le shell, il serait terriblement lent de toute façon.