Échapper à une chaîne pour un modèle sed replace

Dans mon script bash, j’ai une chaîne externe (reçue de l’utilisateur) que je devrais utiliser dans le modèle sed.

REPLACE="" sed "s/KEYWORD/$REPLACE/g" 

Comment puis-je échapper à la chaîne $REPLACE pour qu’elle soit acceptée en toute sécurité par sed comme remplacement littéral?

REMARQUE: Le KEYWORD est une sous-chaîne muette sans correspondance, etc. Elle n’est pas fournie par l’utilisateur.

Attention : Ceci ne prend pas en compte les nouvelles lignes. Pour une réponse plus approfondie, consultez plutôt cette question SO . (Merci, Ed Morton et Niklas Peter)

Notez que tout échapper est une mauvaise idée. Sed a besoin de beaucoup de caractères pour être échappé pour obtenir leur signification particulière. Par exemple, si vous échappez à un chiffre de la chaîne de remplacement, il se transformera en référence arrière.

Comme Ben Blank l’a dit, il n’ya que trois caractères à échapper dans la chaîne de remplacement (les échappements eux-mêmes, la barre oblique pour la fin de l’instruction et & pour remplacer tout):

 sed -e 's/[\/&]/\\&/g' 

Si vous avez déjà besoin d’échapper à la chaîne KEYWORD , vous avez besoin de ce qui suit:

 sed -e 's/[]\/$*.^[]/\\&/g' 

Rappelez-vous que si vous utilisez un caractère autre que / comme délimiteur, vous devez remplacer la barre oblique dans les expressions ci-dessus par le caractère que vous utilisez. Voir le commentaire de PeterJCLaw pour l’explication.

Édité: En raison de certains cas de coin non pris en compte précédemment, les commandes ci-dessus ont changé plusieurs fois. Vérifiez l’historique des modifications pour plus de détails.

La commande sed vous permet d’utiliser d’autres caractères au lieu de / tant que séparateur:

 sed 's#"http://www\.fubar\.com"#URL_FUBAR#g' 

Les guillemets ne posent pas de problème.

Les trois seuls caractères littéraux traités spécialement dans la clause replace sont / (pour fermer la clause), \ (pour échapper des caractères, des références arrière, etc.) et & (pour inclure la correspondance dans le remplacement). Par conséquent, il vous suffit d’échapper à ces trois caractères:

 sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g" 

Exemple:

 $ export REPLACE="'\"|\\/><&!" $ echo fooKEYWORDbar | sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g" foo'"|\/><&!bar 

Basé sur les expressions régulières de Pianosaurus, j’ai créé une fonction bash qui échappe à la fois au mot-clé et au remplacement.

 function sedeasy { sed -i "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3 } 

Voici comment vous l’utilisez:

 sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf 

Il est un peu tard pour réagir … mais il existe un moyen beaucoup plus simple de le faire. Changez simplement le délimiteur (c.-à-d. Le caractère qui sépare les champs). Donc, au lieu de s/foo/bar/ vous écrivez s|bar|foo .

Et voici le moyen facile de le faire:

 sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g' 

La sortie résultante est dépourvue de cette mauvaise clause DEFINER.

Il s’avère que vous posez la mauvaise question. J’ai aussi posé la mauvaise question. La raison pour laquelle c’est faux est le début de la première phrase: “Dans mon script bash …”.

J’ai eu la même question et j’ai fait la même erreur. Si vous utilisez bash, vous n’avez pas besoin d’utiliser sed pour faire des remplacements de chaîne (et il est beaucoup plus propre d’utiliser la fonctionnalité de remplacement intégrée à bash).

Au lieu de quelque chose comme, par exemple:

 function escape-all-funny-characters() { UNKNOWN_CODE_THAT_ANSWERS_THE_QUESTION_YOU_ASKED; } INPUT='some long ssortingng with KEYWORD that need replacing KEYWORD.' A="$(escape-all-funny-characters 'KEYWORD')" B="$(escape-all-funny-characters '')" OUTPUT="$(sed "s/$A/$B/g" <<<"$INPUT")" 

vous pouvez utiliser exclusivement les fonctionnalités de bash:

 INPUT='some long ssortingng with KEYWORD that need replacing KEYWORD.' A='KEYWORD' B='' OUTPUT="${INPUT//"$A"/"$B"}" 

Utilisez awk – c’est plus propre:

 $ awk -v R='//addr:\\file' '{ sub("THIS", R, $0); print $0 }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare" http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare 

Voici un exemple d’un AWK que j’ai utilisé il y a quelque temps. C’est un AWK qui imprime de nouveaux AWKS. AWK et SED étant similaires, cela peut être un bon modèle.

 ls | awk '{ print "awk " "'"'"'" " {print $1,$2,$3} " "'"'"'" " " $1 ".old_ext > " $1 ".new_ext" }' > for_the_birds 

Cela semble excessif, mais d’une certaine manière, cette combinaison de citations permet de conserver les caractères littéraux. Alors, si je me souviens bien, les variables sont juste entourées de guillemets comme ceci: “$ 1”. Essayez-le, laissez-moi savoir comment cela fonctionne avec SED.

Si le cas échéant, vous générez un mot de passe aléatoire à transmettre au modèle sed replace, vous choisissez alors de faire attention au jeu de caractères de la chaîne aléatoire. Si vous choisissez un mot de passe créé en encodant une valeur en base64, il n’y a qu’un seul caractère possible dans base64 et un caractère spécial dans le motif sed replace. Ce caractère est “/” et peut être facilement supprimé du mot de passe que vous générez:

 # password 32 characters log, minus any copies of the "/" character. pass=`openssl rand -base64 32 | sed -e 's/\///g'`; 

J’ai une amélioration par rapport à la fonction sedeasy, qui se cassera avec des caractères spéciaux comme tab.

 function sedeasy_improved { sed -i "s/$( echo "$1" | sed -e 's/\([[\/.*]\|\]\)/\\&/g' | sed -e 's:\t:\\t:g' )/$( echo "$2" | sed -e 's/[\/&]/\\&/g' | sed -e 's:\t:\\t:g' )/g" "$3" } 

Alors, qu’est-ce qui est différent? $1 et $2 entourés de guillemets pour éviter les expansions du shell et préserver les tabulations ou les espaces doubles.

Tuyauterie supplémentaire | sed -e 's:\t:\\t:g' | sed -e 's:\t:\\t:g' (j’aime : comme jeton) qui transforme un onglet dans \t .

Il suffit d’échapper à tout dans le varable REPLACE:

 echo $REPLACE | awk '{gsub(".", "\\\\&");print}' 

n’oublie pas tout le plaisir que procure la limitation de la shell autour de “et”

alors (en ksh)

 Var=">New version of \"content' here <" printf "%s" "${Var}" | sed "s/[&\/\\\\*\\"']/\\&/g' | read -r EscVar echo "Here is your \"text\" to change" | sed "s/text/${EscVar}/g" 

Si vous souhaitez simplement remplacer la valeur de la variable dans la commande sed, il vous suffit de supprimer Exemple:

 sed -i 's/dev-/dev-$ENV/g' test to sed -is/dev-/dev-$ENV/g test 

Un moyen plus simple de le faire consiste simplement à créer la chaîne avant la main et à l’utiliser comme paramètre pour sed

 rpssortingng="s/KEYWORD/$REPLACE/g" sed -i $rpssortingng test.txt