Substituir barra invertida por barra no último comando

10

Copiei o comando cd C:\foo\bar\do PowerShell para o Cygwin e esperançosamente esperei que ele fosse executado. Agora estou tentando executar uma substituição para substituir tudo \por /:

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

Não sei por que estou recebendo a substituição falhou.

Eu também tentei:

$ !!:gs/\\/q

Só para ver se a substituição foi o problema. Não é. Agora estou curioso!

Por que estou recebendo "falha na substituição"?

Tanner Faulkner
fonte
2
Não tenho cygwin à mão agora, mas pensei que os caminhos do Windows estão funcionando ... você já tentou citar o argumento? Ou sejacd 'C:\foo\bar'
mpy
@pypy Isso realmente funciona também! Não sabia que não estava preso à substituição.
precisa saber é o seguinte
1
"Estaria aberto a uma resposta usando o zsh também". Seu original !!:gs/\\/\/funciona em zsh…
Kamil Maciorowski
O @TannerFaulkner fc -s '\'='/' -1funciona no bash padrão do Ubuntu LTS. Deixe-me saber se ele funciona com Cygwin também. Postei mais palavras na resposta.
Hastur 23/01
1
Eu acho que o comportamento geral do bash aqui é que as barras invertidas são processadas como escapes antes da substituição !!:gs/\\/\//. @Hastur fc -s '\'='/' -1obtém o comportamento esperado. isso pode ser um bug no bash.
quixotesca

Respostas:

6

uma barra é também o delimitador do seu comando substituto; portanto, você deve escapar dela como a string de substituição para não terminar a substituição antecipadamente

!!:gs/\\/\//

ou mais claramente, conforme sugerido nos comentários, usando algo além de barra como delimitador

!!:gs|\\|/|

Mas isso parece funcionar apenas tcsh(o shell geralmente designado onde estou), não consigo executar o comando bash, pois parece que escapar não funciona no primeiro argumento de:s

infixado
fonte
Não há alegria :( Mesmo erro i.imgur.com/QFQR0xF.png #
Tanner Faulkner
1
Apenas uma observação: !!:gs|\\|/pode ser um pouco mais legível.
mpy
1
Não consegui fazê-lo funcionar no bash. O primeiro argumento de :snão parece ser um regex real
infixado em 17/17
4

Resposta curta: o builtin fc(comando de correção) do bash

fcé o comando, incorporado no shell bash, criado para editar e reexecutar comandos do histórico.
Também está presente no CygWin e funciona em todas as distribuições Linux nas quais testei:

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

Algumas explicações

  • fcé a festa built-in comando à lista ou editar e re-executar comandos a partir da lista do histórico.
  • -1assumirá o último comando da história. Observe que even !!é definido (lido de man bash) como

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

  • -spara substituir um padrão por outro. Desta vez, a partir de help fc(comando incorporado help):

    Com o formato `fc -s [pat = rep ...] [comando] ', o COMMAND é reexecutado após a substituição OLD = NEW.

    Ok, eles querem dizer em pat=repvez de OLD=NEW...

  • Os padrões serão lidos pelo shell, então temos que protegê-los com aspas: '\'e '/'.

Algumas palavras mais sobre por que você está recebendo "falha na substituição"

Parece que para o s modificador ainda não está implementada a substituição do caractere de barra invertida \, que é o escape. Para ter certeza de que deveríamos ver o código, por exemplo, da versão gnu da expansão do bash history (mas havia o comando acima para obter o que você estava tentando fazer ... então eu tomo preguiçoso ....).

Algumas notas:

  1. Somos levados a pensar que ele funcionará com cada RegEx que encontramos trabalhando sed, mas não é garantido. A barra invertida é o caractere de escape da expansão e o problema está aqui. Além disso, o comportamento da expansão está relacionado às shoptopções, portanto, devemos começar a ver caso a caso ...

  2. Quando você cola a string cd C:\Foo\Barno shell bash, ela será expandida e aparecerá para o intérprete como cd C:FooBar; neste formulário, ele também será armazenado na $_variável interna.
    Se você colou cd "C:\Foo\Bar"ou cd 'C:\Foo\Bar'na $_ variável você deve encontrar C:\Foo\Bar.
    Como a expansão History é executada imediatamente após a leitura de uma linha completa, antes que o shell a divida em palavras, você pode ficar tentado a usá-la com algum basismo mais ou menos claro, por exemplo, com alguma derivação de (talvez adicionar :pou :q, "", análise e assim por diante ...)

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

    É o momento de lembrar que não é seguro começar a brincar com nomes de arquivos e caminhos , principalmente se eles vierem da área de transferência do Windows (leia em geral a página Por que não analisar ls?) , Está essencialmente relacionado à possibilidade de usar a guia, espaços e novas linhas como caracteres corretos para os nomes dos arquivos e dos diretórios ...).
    Além disso, quando você cola um texto capturado com o mouse , também pode colar um espaço à esquerda. Isso pode evitar que o seu comando termine no histórico (depende das opções do shell ...). Nesse caso, seu !!comando a seguir não será controlado ... (veja um exemplo em outra resposta ).Esse é um risco tangível desnecessário .

Conclusão

As expansões do histórico introduzem palavras da lista do histórico no fluxo de entrada, facilitando a repetição de comandos, a inserção de argumentos em um comando anterior na linha de entrada atual ou a correção rápida de erros nos comandos anteriores.

Se não é fácil, começo a pensar que estamos fazendo algo errado ;-)


Ad nauseam: um pequeno experimento

Eu ativei histverifyno shell então ...

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

então eu pressiono Entere como expansão verificada eu acho

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

então eu pressiono Enternovamente e ecoa

D:FooBar 1A 2A

Isso parece indicar que isso substitution failedé gerado na expansão do histórico processada antes da expansão do Brace , então, primeiro de tudo , e parece confirmar que o smodificador do histórico (ainda) não processou (ainda) a substituição do \personagem como um regex real. ..

Hastur
fonte
2

Você pode usar sedpara substituir e executar o resultado.

Existem várias variantes possíveis para esse comando, mas no meu bash apenas este funcionou:

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

O \\é adicionado ao !!is no caso do último comando terminar com uma barra invertida. Sem ele, o shell ficará confuso e solicitará a continuação da linha.

Você também pode adicionar isso como uma função bash no arquivo ~/.bashrc:

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

Aqui está como isso funciona no meu Bash no Windows:

bater

Para explicar como o A=comando atribui o valor correto à variável do shell A:

  • tailpega as duas últimas linhas do histórico de comandos, que fornece as linhas de cd \mnt\cseguido por slashele mesmo. A parte de history | tail -n 2também pode ser escrita como history 2.
  • head pegue a primeira linha que é cd \mnt\c
  • cut corta o número da linha fornecido pela history
  • sed substitui todos os anti-barras por barras
  • eval executa o conteúdo da variável A
harrymc
fonte
Olá Harry. Lamento dizer que na minha versão do GNU bash 4.3.11 (1) o A=$(echo "!!\\" | sed 's,\\,/,g');eval $Anão funciona quando o comando foi finalizado com uma barra invertida. Você é forçado a adicionar um espaço, por exemplo, ao C:\Foo\Bar\ else, como você disse, o shell aguardará a continuação da linha. Você também pode pressionar enter duas vezes. No primeiro caso, você obtém C:/Foo/Bar /(com um espaço antes do /; no segundo ele gera um erro. A função funciona bem.
Hastur
Eu escrevi a função porque (1) é muito mais fácil de usar e (2) o one-liner parecia muito dependente da implementação.
precisa saber é
-1

Eu não acho que você pode fazer isso. Você precisa copiar / colar novamente.

O problema não está relacionado à barra sendo também o separador de comando substituto, pois o comando substituto aceita qualquer separador. A questão é sobre as barras invertidas a serem substituídas, que já foram tratadas como simples escapamentos inúteis de caráter à sua direita.

Portanto, quando você chama o comando substitute, a barra invertida já desaparece da fonte. Apenas tente chamar novamente o comando como está ( !!). Em vez de imprimir exatamente o mesmo comando c:\foo, ele executará o comando menos as barras invertidas já processadas que resultam em c:foo.

E, obviamente, você não pode encontrar nem substituir um personagem ausente. Tentar fazer isso disparará um erro.

A. Loiseau
fonte
1
Bem. Finalmente, isso está errado, pois esse teste realmente funciona: echo c:\Fooseguido por !!:gs,F,\\f,. Mas Substituir a própria barra invertida parece ser uma dor.
A. Loiseau 17/01