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 from man bash) as

    !! Refer to the previous command. This is a synonym for!-1'.

  • -s to substitute a pattern with another. This time from help fc (built-in command so help):

    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 of OLD=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:

  1. 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 the shopt options, so we should start to see case by case...

  2. When you paste the string cd C:\Foo\Bar in your bash shell it will be expanded and it will appear for the interpreter as cd C:FooBar; in this form it will be stored in the $_ internal variable too.
    If you instead pasted cd "C:\Foo\Bar" or cd 'C:\Foo\Bar' in the $_ variable you should find C:\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 :

bash

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 of cd \mnt\c followed by slash itself. The part of history | tail -n 2 may also be written as history 2.
  • head take the first line which is cd \mnt\c
  • cut cuts out the line-number supplied by history
  • sed replaces all anti-slashes by slashes
  • eval executes the contents of the variable A