J'ai copié la commande cd C:\foo\bar\
de PowerShell vers Cygwin et je m'attendais à ce qu'elle s'exécute. J'essaie maintenant d'exécuter une substitution pour remplacer tous les éléments \
par /
:
$ !!:gs/\\/\/
bash: :gs/\\/\/: substitution failed
Je ne sais pas pourquoi j'obtiens la substitution a échoué.
J'ai aussi essayé:
$ !!:gs/\\/q
Juste pour voir si le remplacement était le problème. Ce n'est pas. Maintenant je suis curieux!
Pourquoi est-ce que j'obtiens "la substitution a échoué"?
cd 'C:\foo\bar'
!!:gs/\\/\/
fonctionne en zsh…fc -s '\'='/' -1
fonctionne sous le bash par défaut d'Ubuntu LTS. Faites-moi savoir si cela fonctionne aussi sous Cygwin. J'ai posté plus de mots dans la réponse.!!:gs/\\/\//
. @Hasturfc -s '\'='/' -1
obtient le comportement attendu. cela pourrait être un bogue dans bash.Réponses:
une barre oblique est également le délimiteur de votre commande de remplacement, vous devez donc l'échapper en tant que chaîne de remplacement afin de ne pas terminer la substitution plus tôt
ou plus simplement, comme suggéré dans les commentaires, en utilisant autre chose qu'une barre oblique comme délimiteur
Mais cela ne semble fonctionner que dans
tcsh
(le shell généralement assigné où je suis), je ne peux pas faire fonctionner la commandebash
car il semble que l'échappement ne fonctionne pas sur le premier argument de:s
la source
!!:gs|\\|/
pourrait être un peu plus lisible.:s
ne semble pas être un vrai regexRéponse courte: la fonction intégrée
fc
(commande fix) de bashfc
est la commande, intégrée au shell bash, conçue pour éditer et réexécuter les commandes de l'historique.Il est également présent sur CygWin et fonctionne sur toutes les distributions Linux sur lesquelles j'ai testé:
Quelques explications
fc
est le bash intégré commande à la liste ou modifier et re-exécuter des commandes à partir de la liste d'historique.-1
prendra la dernière commande de l'histoire. Notez que even!!
est défini (lu depuisman bash
) comme-s
pour remplacer un motif par un autre. Cette fois à partir dehelp fc
(commande intégrée donchelp
):Ok ils veulent dire
pat=rep
au lieu deOLD=NEW
...'\'
et'/'
.Quelques mots de plus sur les raisons de l'échec de la substitution
Il semble que pour le
s
modificateur ne soit pas (encore) implémenté la substitution du caractère barre oblique inverse\
, c'est le caractère d'échappement. Pour être sûr, nous devrions voir le code, par exemple, de la version gnu de l'expansion de l'historique bash (mais il y avait la commande ci-dessus pour obtenir ce que vous essayez de faire ... alors je le prends paresseux ....).Quelques notes:
Nous sommes amenés à penser que cela fonctionnera avec chaque RegEx avec lequel nous travaillons
sed
, mais ce n'est pas garanti. La barre oblique inverse est le caractère d'échappement de l'extension et le problème est là. De plus le comportement de l'expansion est lié auxshopt
options, il faut donc commencer à voir au cas par cas ...Lorsque vous collez la chaîne
cd C:\Foo\Bar
dans votre shell bash, elle sera développée et apparaîtra pour l'interpréteur commecd C:FooBar
; sous cette forme, il sera également stocké dans la$_
variable interne.Si vous avez plutôt collé
cd "C:\Foo\Bar"
oucd 'C:\Foo\Bar'
dans la$_
variable, vous devriez trouverC:\Foo\Bar
.Étant donné que l'expansion de l'historique est effectuée immédiatement après la lecture d'une ligne complète, avant que le shell ne la décompose en mots, vous pouvez être tenté de commencer à l'utiliser avec un bashisme plus ou moins simple, par exemple, avec une dérivation de (peut-être en ajoutant
:p
ou:q
,""
, l'analyse et ainsi de suite ...)C'est le moment de se rappeler qu'il n'est pas sûr de commencer à jouer avec les chemins et les noms de fichiers , surtout s'ils proviennent du presse-papiers de Windows (lire en général la page Pourquoi ne pas analyser
ls
?, C'est essentiellement lié à la possibilité d'utiliser tab, espaces et sauts de ligne comme caractères corrects pour les noms de fichiers et de répertoires ...).De plus, lorsque vous collez un texte capturé avec la souris , vous pouvez également coller un espace de tête. Cela peut éviter que votre commande se termine dans l'historique (cela dépend des options du shell ...). Si c'est le cas, votre commande suivante
!!
sera une commande non contrôlée ... (voir un exemple dans une autre réponse ).Il s'agit d'un risque tangible inutile .Conclusion
Si ce n'est pas facile, je commence à penser que nous faisons quelque chose de mal
;-)
Ad nauseam: une petite expérience
J'ai activé
histverify
dans le shell alors ...puis j'appuie Enteret comme expansion vérifiée je trouve
puis j'appuie à Enternouveau et ça fait écho
Cela semble indiquer que le
substitution failed
est généré dans l'expansion de l'histoire traitée avant l' expansion de Brace , donc tout d'abord , et cela semble confirmer que les
modificateur de l' histoire n'a pas (encore) traité la substitution du\
personnage comme une véritable expression régulière. ..la source
Vous pouvez utiliser
sed
pour remplacer et exécuter le résultat.Il existe plusieurs variantes possibles pour une telle commande, mais sur mon bash seule celle-ci a fonctionné:
Le
\\
est ajouté à l'!!
est au cas où la dernière commande se terminerait par une barre oblique inverse. Sans cela, le shell se confondra et demandera la poursuite de la ligne.Vous pouvez également ajouter ceci en tant que fonction bash dans le fichier
~/.bashrc
:Voici comment cela fonctionne sur mon Bash sous Windows:
Pour expliquer comment la
A=
commande attribue la bonne valeur à la variable shellA
:tail
prend les deux dernières lignes de l'historique des commandes, ce qui donne les lignes decd \mnt\c
suivi deslash
lui-même. La partie dehistory | tail -n 2
peut également s'écrirehistory 2
.head
prenez la première ligne qui estcd \mnt\c
cut
supprime le numéro de ligne fourni parhistory
sed
remplace tous les anti-slash par des slasheseval
exécute le contenu de la variableA
la source
A=$(echo "!!\\" | sed 's,\\,/,g');eval $A
ne fonctionne pas lorsque la commande s'est terminée avec une barre oblique inverse. Vous êtes obligé d'ajouter un espace, par exemple àC:\Foo\Bar\
else, comme vous l'avez dit, le shell attendra la suite de la ligne. Vous pouvez également appuyer deux fois sur Entrée. Dans le premier cas, vous obtenezC:/Foo/Bar /
(avec un espace avant le/
; dans le second, cela générera une erreur. La fonction fonctionne bien.Je ne pense pas que vous puissiez faire ça. Vous devez copier / coller à nouveau.
Le problème n'est pas lié à la barre oblique étant également le séparateur de commande de substitution, car la commande de substitution accepte tout séparateur. Le problème concerne les barres obliques inversées à remplacer, qui ont déjà été traitées comme de simples échappées inutiles de caractère à leur droite.
Par conséquent, lorsque vous appelez la commande de substitution, la barre oblique inverse est déjà supprimée de la source. Essayez simplement de rappeler la commande telle quelle (
!!
). Au lieu d'imprimer exactement la même commande, y comprisc:\foo
, il exécutera la commande moins les barres obliques inverses déjà traitées qui en résulterontc:foo
.Et évidemment, vous ne pouvez pas trouver ni remplacer un caractère manquant. Essayer de le faire déclenchera une erreur.
la source
echo c:\Foo
suivi de!!:gs,F,\\f,
. Mais la substitution de la barre oblique inverse semble être une douleur.