Ceci est une amélioration de la réponse de Hashbrown (et la réponse de wef à une question très similaire).
Nous pouvons éliminer le problème du sens spécial de divers caractères spéciaux et chaînes (^
, .
, [
, *
, $
, \(
, \)
, \{
, \}
, \+
, \?
, &
, \1
, …, peu importe, et le délimiteur /
) en supprimant les caractères spéciaux. En particulier, nous pouvons tout convertir en hexadécimal ; ensuite nous n'avons plus que les caractères 0
-9
et a
-f
à traiter. Cet exemple démontre le principe :
$ echo -n '3.14' | xxd
0000000: 332e 3134 3.14
$ echo -n 'pi' | xxd
0000000: 7069 pi
$ echo '3.14 is a transcendental number. 3614 is an integer.' | xxd
0000000: 332e 3134 2069 7320 6120 7472 616e 7363 3.14 is a transc
0000010: 656e 6465 6e74 616c 206e 756d 6265 722e endental number.
0000020: 2020 3336 3134 2069 7320 616e 2069 6e74 3614 is an int
0000030: 6567 6572 2e0a eger..
$ echo "3.14 is a transcendental number. 3614 is an integer." | xxd -p \
| sed 's/332e3134/7069/g' | xxd -p -r
pi is a transcendental number. 3614 is an integer.
alors que bien sûr, sed 's/3.14/pi/g'
changerait aussi 3614
.
Ci-dessus est une légère simplification ; elle ne tient pas compte des limites. Considérez cet exemple (quelque peu artificiel) :
$ echo -n 'E' | xxd
0000000: 45 E
$ echo -n 'g' | xxd
0000000: 67 g
$ echo '$Q Eak!' | xxd
0000000: 2451 2045 616b 210a $Q Eak!.
$ echo '$Q Eak!' | xxd -p | sed 's/45/67/g' | xxd -p -r
&q gak!
Parce que $
(24
) et Q
(51
) se combinent pour former 2**_45_**1
, la commande s/45/67/g
le déchire de l'intérieur. Elle change 2451
en 2671
, qui est &q
(26
+71
). Nous pouvons empêcher cela en séparant les octets de données dans le texte de recherche, le texte de remplacement et le fichier avec des espaces. Voici une solution stylisée :
encode() {
xxd -p -- "$@" | sed 's/../& /g' | tr -d '\n'
}
decode() {
xxd -p -r -- "$@"
}
left=$( printf '%s' "$search" | encode)
right=$(printf '%s' "$replacement" | encode)
encode path/to/the/file | sed "s/$left/$right/g" | decode
J'ai défini une fonction encode
car j'ai utilisé cette fonctionnalité trois fois, puis j'ai défini decode
pour la symétrie. Si vous ne voulez pas définir une fonction decode
, changez simplement la dernière ligne en
encode path/to/the/file | sed "s/$left/$right/g" | xxd -p –r
Remarquez que la fonction encode
triple la taille des données (texte) dans le fichier, puis les envoie à travers sed
en une seule ligne — sans même avoir un retour à la ligne à la fin. GNU sed semble être capable de gérer cela ; d'autres versions pourraient ne pas y parvenir. En outre, cela ne modifie pas le fichier sur place ; vous devrez écrire la sortie dans un fichier temporaire et ensuite le copier sur le fichier original (ou l'une des autres astuces pour faire cela).
En bonus, cette solution gère les recherches et remplacements sur plusieurs lignes (en d'autres termes, des chaînes de recherche et de remplacement qui contiennent des sauts de ligne).