Introdução
Considere uma ferramenta como watch
essa que pode receber outro comando (por exemplo ls -l
) como este:
watch ls -l
# or equivalently
watch 'ls -l'
Se o comando for mais complicado, trata-se de aspas/escape se coisas como $variable
, *
, |
ou forem interpretadas pelo shell atual ou get ;
to . Esses dois comandos são diferentes:&&
watch
watch echo "$$"
watch 'echo "$$"'
Assim são estes:
watch date ; echo done
watch date \; echo done
ssh
é semelhante, pode receber um ou mais argumentos e criar um comando para ser executado no servidor. Depende de aspas/escape se o shell local interpreta $variable
, |
e tal, ou o shell remoto.
E há comandos como sh -c
os que requerem um único argumento contendo código, mas a necessidade de aspas/escape é semelhante. Na verdade , watch
(ou ssh
) cria esse único argumento para sh -c
um comando semelhante.
Problema
Já tenho o comando exato que desejo fornecer para watch
ou ssh
, ou ferramenta semelhante. O comando é obtido do histórico ou colado na linha de comando ou digitado como se fosse ser executado diretamente; está na minha linha de comando literalmente. Nada no comando deve ser expandido ou interpretado antes de chegar a watch
uma ferramenta semelhante. Caso watch
isso signifique toda a ampliação e interpretação deverá ocorrer posteriormente, periodicamente. Caso ssh
isso signifique que deve ocorrer no servidor.
Agora preciso fazer duas coisas:
- Adicione
watch
(ou o que for) no início. Isso não é um problema. Eu posso fazer isso por último. Aspas e/ou escape do comando original. Em geral, o comando pode incluir aspas simples, portanto, apenas adotar aspas simples cegamente não é uma solução. Acho que conheço a solução geral certa:
- substitua cada
'
por'"'"'
ou por'\''
, - inclua toda a string resultante com aspas simples;
mas é tedioso e sujeito a erros fazer isso manualmente.
- substitua cada
Pergunta
Posso fazer com que o Bash adicione corretamente um nível de aspas simples/escape a toda a linha de comando sob demanda?
(Nota: a pergunta surgiu quando eu estava respondendo a esta .)
Solução
Sim. Minha ideia é chamar uma função de shell (ao pressionar uma tecla) que manipulará
READLINE_LINE
usando o${variable@Q}
recurso.Partes relevantes da documentação:
( fonte )
( fonte )
O seguinte funciona no Bash 4.4.20:
Para testar a solução, prepare um comando na linha de comando (não execute), por exemplo
(A citação e todo o comando podem ser simplificados. É deliberadamente assim. E você pode executá-lo para garantir que seja um comando válido, mas coloque-o de volta na linha de comando antes de prosseguir.)
Pressione Ctrl+ x, Ctrl+ oe ele será devidamente citado/escapado para o nosso propósito. Isso parecerá assim:
Agora tudo que você precisa fazer é adicionar
watch
(oussh …
, ou o que for) na frente e executar. Se for,watch
observe que o cabeçalho é comoEle contém o comando original. O comando chegou corretamente a
watch
, nenhuma parte foi interpretada prematuramente.Melhorias
Por conveniência, considere esta variante:
Ele preparará a linha e colocará o cursor no início, para que você possa digitar
watch
imediatamente. Ou talvez até mesmo esta variante (ela deliberadamente tem um nome diferente, estamos criando uma ligação separada para ela):Agora Ctrl+ x, Ctrl+ wlida com citações, insere
watch
automaticamente e coloca o cursor na posição certa para você digitar opções.Com mais uma função usando
READLINE_POINT
é possível lidar com o seguinte cenário: digitewatch
(oussh …
) seguido de um comando, onde aspas/escape é como se o comando fosse executado diretamente. Coloque o cursor onde o comando começa, pressione a tecla e deixe a função modificar tudo, desde o cursor até o final da linha. Não estou fornecendo essa função aqui; escreva você mesmo se precisar.Yo Dawg, ouvi dizer que você gosta de citações
Você pode empilhar a solução. Quero dizer, você pode ir a partir disso
para isso
para isso
para isso
(sim, eu sei
ssh -J
)pressionando apenas Ctrl+ x, Ctrl+ oe acrescentando uma ou algumas palavras na frente em cada etapa.