Substitute Backslash for Forward slash in last command
Solution 1:
a forward slash is also the delimiter of your substitute command, so you must escape it as the replacement string so as not to end the substitution early
!!:gs/\\/\//
or more plainly as suggested in the comments, by using something besides slash as the delimiter
!!:gs|\\|/|
But this only seems to work in tcsh
(the shell commonly assigned where I am), I can't get the command to work in bash
as it seems escaping doesn't work on the first argument of :s
Solution 2:
Short answer: the builtin fc
(fix command) of bash
fc
is the command, built-in in the bash shell, made to edit & re-execute commands of the history.
It is present on CygWin too and it works on all the Linux distributions on which I tested:
fc -s '\'='/' -1
Some explanations
-
fc
is the bash built-in command to list or edit and re-execute commands from the history list. -
-1
will take the last command in the history. Note that even!!
is defined (read fromman bash
) as!! Refer to the previous command. This is a synonym for
!-1'. -
-s
to substitute a pattern with another. This time fromhelp fc
(built-in command sohelp
):With the `fc -s [pat=rep ...] [command]' format, COMMAND is re-executed after the substitution OLD=NEW is performed.
Ok they mean
pat=rep
instead ofOLD=NEW
... - The patterns will be read by the shell so we have to protect them with quote:
'\'
and'/'
.
Some words more about why you are getting "substitution failed"
It seems that for the s
modifier is not (yet) implemented the substitution of the backslash character \
, that is the escape one. To be sure we should see the code, for example, of the gnu version of the bash history expansion (but there was the above command to obtain what you were trying to do...so I take it lazy....).
Some notes:
We are lead to think that it will work each RegEx we find working with
sed
, but it is not guaranteed. The backslash is the escape character of the expansion and the problem is here. Moreover the behaviour of the expansion is related with theshopt
options, so we should start to see case by case...-
When you paste the string
cd C:\Foo\Bar
in your bash shell it will be expanded and it will appear for the interpreter ascd C:FooBar
; in this form it will be stored in the$_
internal variable too.
If you instead pastedcd "C:\Foo\Bar"
orcd 'C:\Foo\Bar'
in the$_
variable you should findC:\Foo\Bar
.
Since the History expansion is performed immediately after a complete line is read, before the shell breaks it into words, you may be tempted to start to use it with some bashism more or less plain, e.g., with some derivation from (maybe adding:p
or:q
,""
, parsing and so on...)!!:0 ${_//\\/\/}
This is the moment to remember that it is not safe to start to play with path and filenames, especially if they come from the windows clipboard (read in general the page Why not parse
ls
?, it is essentially related with the possibility to use tab, spaces and newlines as correct characters for the file names and the directory ones...).
Moreover when you paste a text captured with the mouse, you may paste a leading space too. This may avoid that your command will finish in the history (it depends from the shell options...). If so your following!!
will be a not controlled command... (see an example in another answer). This is a unneeded tangible risk.
Conclusion
History expansions introduce words from the history list into the input stream, making it easy to repeat commands, insert the arguments to a previous command into the current input line, or fix errors in previous commands quickly.
If it is not easy I start to think we are doing something wrong ;-)
Ad nauseam: a little experiment
I've enabled histverify
in the shell then...
shopt -s histverify
echo C:\Foo\Bar
!!:s|C|D| {1,2}A
then I press Enter and as verified expansion I find
echo D:\Foo\Bar {1,2}A
then I press Enter again and it echoes
D:FooBar 1A 2A
This seems to indicate that the substitution failed
is generated in the history expansion processed before of the Brace expansion, so first of all, and it seems to confirm that the s
history modifier didn't (yet) process the substitution of the \
character as a real regex...
Solution 3:
You can use sed
to substitute and execute the result.
There are several possible variants for such a command, but on my bash only this one worked:
A=$(echo "!!\\" | sed 's,\\,/,g');eval $A
The \\
is added to the !!
is in case the last command terminated with a
backslash. Without it, the shell will get confused and ask for the continuation
of the line.
You could also add this as a bash function in the file ~/.bashrc
:
function slash {
A=$(history | tail -n 2 | head -n 1 | cut -c 8- | sed 's,\\,/,g')
eval $A
}
Here is how this works on my Bash on Windows :
To explain how the A=
command assigns the right value to the shell variable A
:
-
tail
takes the last two lines from the command history, which gives the lines ofcd \mnt\c
followed byslash
itself. The part ofhistory | tail -n 2
may also be written ashistory 2
. -
head
take the first line which iscd \mnt\c
-
cut
cuts out the line-number supplied byhistory
-
sed
replaces all anti-slashes by slashes -
eval
executes the contents of the variableA