Je cherche un moyen de remplacer les chaînes d'espace réservé dans un fichier de modèle par des valeurs concrètes, avec des outils Unix courants (bash, sed, awk, peut-être perl). Il est important que le remplacement soit effectué en un seul passage, c'est-à-dire que ce qui est déjà numérisé / remplacé ne doit pas être pris en compte pour un autre remplacement. Par exemple, ces deux tentatives échouent:
echo "AB" | awk '{gsub("A","B");gsub("B","A");print}'
>> AA
echo "AB" | sed 's/A/B/g;s/B/A/g'
>> AA
Le résultat correct dans ce cas est bien sûr BA.
En général, la solution doit être équivalente à balayer l'entrée de gauche à droite pour une correspondance la plus longue avec l'une des chaînes de remplacement données, et pour chaque correspondance, effectuer un remplacement et continuer à partir de ce point dans l'entrée (aucune des déjà lu, ni les remplacements effectués ne doivent être pris en compte pour les correspondances). En fait, les détails n'ont pas d'importance, juste que les résultats du remplacement ne sont jamais pris en compte pour un autre remplacement, en tout ou en partie.
REMARQUE Je recherche uniquement des solutions génériques correctes. Veuillez ne pas proposer de solutions qui échouent pour certaines entrées (fichiers d'entrée, recherche et remplacement de paires), aussi improbables qu'elles puissent paraître.
tr AB BA
.Réponses:
OK, une solution générale. La fonction bash suivante nécessite des
2k
arguments; chaque paire se compose d'un espace réservé et d'un remplacement. C'est à vous de citer les chaînes de façon appropriée pour les passer dans la fonction. Si le nombre d'arguments est impair, un argument vide implicite sera ajouté, ce qui supprimera efficacement les occurrences du dernier espace réservé.Ni les espaces réservés ni les remplacements ne peuvent contenir de caractères NUL, mais vous pouvez utiliser des C-
\
Escapes standard, par exemple\0
si vous avez besoin deNUL
s (et par conséquent, vous devez écrire\\
si vous voulez un\
).Il nécessite les outils de construction standard qui devraient être présents sur un système de type posix (lex et cc).
Nous supposons qu'il
\
est déjà échappé si nécessaire dans les arguments, mais nous devons échapper les guillemets doubles, le cas échéant. C'est ce que fait le deuxième argument du second printf. Puisque l'lex
action par défaut estECHO
, nous n'avons pas à nous en préoccuper.Exemple d'exécution (avec des horaires pour les sceptiques; c'est juste un ordinateur portable bon marché):
Pour des entrées plus importantes, il pourrait être utile de fournir un indicateur d'optimisation
cc
et, pour la compatibilité Posix actuelle, il serait préférable d'utiliserc99
. Une implémentation encore plus ambitieuse pourrait essayer de mettre en cache les exécutables générés au lieu de les générer à chaque fois, mais ils ne sont pas exactement coûteux à générer.Éditer
Si vous avez tcc , vous pouvez éviter les tracas de la création d'un répertoire temporaire et profiter du temps de compilation plus rapide qui vous aidera sur les entrées de taille normale:
la source
fn() { tcc ; } <<CODE\n$(gen code)\nCODE\n
. Puis-je demander - c'est une réponse géniale et je l'ai votée dès que je l'ai lue - mais je ne comprends pas ce qui se passe avec le tableau de shell? Qu'est-"${@//\"/\\\"}"
ce que cela fait?Quelque chose comme ça ne remplacera toujours chaque occurrence de vos chaînes cibles qu'une seule fois car elles se produisent dans
sed
's in stream à une bouchée par ligne. C'est le moyen le plus rapide que j'imagine que vous feriez. Là encore, je n'écris pas C. Mais cela gère de manière fiable les délimiteurs nuls si vous le souhaitez. Voir cette réponse pour savoir comment cela fonctionne. Cela ne pose aucun problème avec les caractères de shell spéciaux contenus ou similaires - mais il est spécifique aux paramètres régionaux ASCII ou, en d'autres termes,od
ne produira pas de caractères multi-octets sur la même ligne et n'en fera qu'un par. Si c'est un problème, vous voudrez l'ajoutericonv
.la source
sed
et enregistrer jusqu'à une valeur nulle ou quelque chose, puis fairesed
écrire le script de celui-ci; ou le mettre dans une fonction shell et lui donner des valeurs à une bouchée par ligne comme"/$1/"
..."/$2/"
- peut-être que j'écrirai ces fonctions aussi ...PLACE1
,PLACE2
etPLA
.PLA
gagne toujours. OP dit: "équivalent à balayer l'entrée de gauche à droite pour une correspondance la plus longue avec l'une des chaînes de remplacement données" (non souligné dans l'original)Une
perl
solution. Même si certains ont déclaré que ce n'était pas possible, j'en ai trouvé un, mais en général, une simple correspondance et remplacement n'est pas possible et même cela empire en raison du retour en arrière d'un NFA, le résultat peut être inattendu.En général, et cela doit être dit, le problème donne des résultats différents qui dépendent de l'ordre et de la longueur des tuples de remplacement. c'est à dire:
et l'entrée se
AAA
traduit parBBB
ouCCB
.Voici le code:
Checkerbunny:
la source