Sustituya la barra diagonal inversa por una barra inclinada en el último comando

10

He copiado el comando cd C:\foo\bar\ de PowerShell a Cygwin y Sillily esperaba que se ejecutara. Ahora estoy tratando de ejecutar una sustitución para reemplazar todos los \ con /:

$ !!:gs/\\/\/
bash: :gs/\\/\/: substitution failed

No estoy seguro de por qué estoy recibiendo la sustitución fallida.

También intenté:

$ !!:gs/\\/q

Sólo para ver si el reemplazo fue el problema. No es. ¡Ahora estoy curioso!

¿Por qué estoy recibiendo "sustitución fallida"?

Tanner Faulkner
fuente
2
No tengo Cygwin a la mano en este momento, pero pensé que las rutas de Windows están funcionando ... ¿has tratado de citar el argumento de forma individual? Es decir. cd 'C:\foo\bar'
mpy
@mpy ¡Esto también funciona! No sabía que no estaba atascado con la sustitución.
Tanner Faulkner
1
"Estaría abierto a una respuesta usando zsh también". Su original !!:gs/\\/\/ funciona en zsh ...
Kamil Maciorowski
@TannerFaulkner fc -s '\'='/' -1 funciona bajo el bash predeterminado de Ubuntu LTS. Déjame saber si funciona bajo Cygwin también. Publiqué más palabras en la respuesta.
Hastur
1
Creo que el comportamiento general de bash aquí es que las barras invertidas se procesan como escapes antes de que ocurra la sustitución en !!:gs/\\/\//. @Hastur's fc -s '\'='/' -1 Obtiene el comportamiento esperado. esto podría ser un error en bash.
quixotic

Respuestas:

6

una barra diagonal hacia delante también es el delimitador de su comando de sustitución, por lo que debe escapar como una cadena de reemplazo para no finalizar la sustitución antes de tiempo

!!:gs/\\/\//

o más claramente como se sugiere en los comentarios, usando algo además de la barra como el delimitador

!!:gs|\\|/|

Pero esto solo parece funcionar en tcsh (el shell asignado comúnmente donde estoy), no puedo obtener el comando para trabajar en bash Como parece escapar, no funciona en el primer argumento de :s

infixed
fuente
No alegría :( mismo error i.imgur.com/QFQR0xF.png
Tanner Faulkner
1
Sólo un comentario: !!:gs|\\|/ podría ser un poco más legible.
mpy
1
No pude hacerlo funcionar en bash. El primer arg de :s no parece ser un regex real
infixed
4

Respuesta corta: la incorporada fc (arreglar comando) de bash

fc es el comando, incorporado en el shell bash, hecho para editar & amp; volver a ejecutar los comandos de la historia.
Es Presente en CygWin también y funciona en todas las distribuciones de Linux en las que he probado:

fc  -s '\'='/' -1

Algunas explicaciones

  • fc es la fiesta incorporado mando para listar o editar y volver a ejecutar comandos de la lista de historial.
  • -1 Tomará el último comando en la historia. Tenga en cuenta que incluso !! está definido (leído de man bash ) como

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

  • -s Sustituir un patrón por otro. Esta vez de help fc (comando incorporado para help ):

    Con el formato `fc -s [pat = rep ...] [comando] ', el COMANDO es   se vuelve a ejecutar después de que se realiza la sustitución OLD = NEW.

    Ok ellos quieren decir pat=rep en lugar de OLD=NEW ...

  • Los patrones serán leídos por el shell, así que tenemos que protegerlos con una cita: '\' y '/'.

Algunas palabras más acerca de por qué está obteniendo "sustitución fallida"

Parece que para el s modificador no se ha implementado (todavía) la sustitución del carácter de barra invertida \, ese es el escape. Para asegurarnos de que deberíamos ver el código, por ejemplo, de la versión gnu de la expansión del historial de bash (pero estaba el comando anterior para obtener lo que estaba intentando hacer ... así que tómalo perezoso ....).

Algunas notas:

  1. Nos llevan a pensar que funcionará cada RegEx que encontremos trabajando con sed, pero no está garantizado. La barra invertida es el carácter de escape de la expansión y el problema está aquí. Además, el comportamiento de la expansión está relacionado con la shopt Opciones, por lo que deberíamos empezar a ver caso por caso ...

  2. Cuando pegas la cadena cd C:\Foo\Bar en su shell bash se ampliará y aparecerá para el intérprete como cd C:FooBar; de esta forma se almacenará en el $_ variable interna también.
    Si en lugar de pegar cd "C:\Foo\Bar" o cd 'C:\Foo\Bar' en el $_ variable que deberías encontrar C:\Foo\Bar.
    Dado que la expansión del Historial se realiza inmediatamente después de leer una línea completa, antes de que el shell la rompa en palabras, puede tener la tentación de comenzar a usarla con algunos bashismo más o menos simple, por ejemplo, con alguna derivación de (tal vez agregando :p o :q, "", analizando y así sucesivamente ...)

    !!:0 ${_//\\/\/}
    

    Este es el momento para recordar que no es seguro. para comenzar jugar con camino y nombres de archivos , especialmente si vienen del portapapeles de Windows (lea en general la página Por qué no analizar gramaticalmente ls? , está esencialmente relacionado con la posibilidad de usar tabulaciones, espacios y nuevas líneas como caracteres correctos para los nombres de archivo y los de directorio ...).
    Además cuando Pegar un texto capturado con el ratón. , también puedes pegar un espacio inicial. Esto puede evitar que su comando termine en el historial (depende de las opciones de shell ...). Si es así tu siguiente !! será un comando no controlado ... (ver un ejemplo en otra respuesta ). Este es un riesgo tangible innecesario. .

Conclusión

Expansiones de historia introducir palabras de la lista de historial en el flujo de entrada, haciéndolo fácil para repetir comandos, inserte los argumentos de un comando anterior en la línea de entrada actual o corrija los errores en los comandos anteriores rápidamente.

Si no es fácil empiezo a pensar que estamos haciendo algo mal. ;-)


Ad nauseam: un pequeño experimento.

He habilitado histverify en la cáscara entonces ...

shopt -s histverify
echo C:\Foo\Bar
!!:s|C|D| {1,2}A

entonces presiono Entrar y como verificado expansión encuentro

echo D:\Foo\Bar {1,2}A

entonces presiono Entrar de nuevo y se hace eco

D:FooBar 1A 2A

Esto parece indicar que la substitution failed Se genera en la historia la expansión procesada antes de la Expansión de la abrazadera , asi que ante todo , y parece confirmar que la s el modificador de historia (todavía) no procesó la sustitución del \ personaje como regex real ...

Hastur
fuente
2

Puedes usar sed Sustituir y ejecutar el resultado.

Hay varias variantes posibles para este comando, pero en mi caso solo este funcionó:

A=$(echo "!!\\" | sed 's,\\,/,g');eval $A

los \\ se agrega a la !! es en el caso de que el último comando terminó con una barra invertida Sin ella, la concha se confundirá y pedirá la continuación. de la linea.

También puede agregar esto como una función bash en el archivo ~/.bashrc :

function slash {
   A=$(history | tail -n 2 | head -n 1 | cut -c 8- | sed 's,\\,/,g')
   eval $A
}

Aquí es cómo funciona esto en mi Bash en Windows:

bash

Para explicar cómo el A= comando asigna el valor correcto a la variable de shell A:

  • tail toma las dos últimas líneas del historial de comandos, lo que da la líneas de cd \mnt\c seguido por slash sí mismo. La parte de history | tail -n 2 también puede ser escrito como history 2.
  • head tomar la primera línea que es cd \mnt\c
  • cut corta el número de línea suministrado por history
  • sed Reemplaza todos los anti-slashes por slashes
  • eval Ejecuta los contenidos de la variable. A
harrymc
fuente
Hola Harry. Lamento decir que en mi versión bash de GNU 4.3.11 (1) la A=$(echo "!!\\" | sed 's,\\,/,g');eval $A no funciona cuando el comando terminó con una barra invertida. Se te obliga a agregar un espacio, por ejemplo. a C:\Foo\Bar\ más, como dijiste, el shell esperará la continuación de la línea. También puedes pulsar enter dos veces. En el primer caso obtienes C:/Foo/Bar / (con un espacio antes de la /; En el segundo generará un error. La función funciona bien.
Hastur
Escribí la función porque (1) es mucho más fácil de usar, y (2) el one-liner parecía demasiado dependiente de la implementación.
harrymc
-1

No creo que puedas hacer esto. Necesitas copiar / pegar de nuevo.

El problema no está relacionado con la barra inclinada, sino también con el separador de comando de sustitución, ya que el comando de sustitución acepta cualquier separador. El problema es sobre las barras diagonales que se sustituirán, que ya han sido tratadas como simples escapes de caracteres inútiles a su derecha.

Por lo tanto, cuando llama al comando de sustitución, la barra invertida ya ha desaparecido de la fuente. Solo intente volver a llamar el comando tal como está ( !! ). En lugar de imprimir el mismo comando exacto incluyendo c:\foo, ejecutará el comando menos las barras invertidas ya procesadas que resultan en c:foo.

Y obviamente no puedes encontrar ni reemplazar un personaje perdido. Tratar de hacerlo disparará un error.

A. Loiseau
fuente
1
Bien. Eso es finalmente incorrecto ya que esta prueba realmente funciona: echo c:\Foo seguido por !!:gs,F,\\f,. Pero Sustituir el escape de barra invertida en sí parece ser un dolor.
A. Loiseau