Uma coisa a ter em mente é que o nome do arquivo é passado não modificado para o shell de login do usuário remoto.
Se você fizer isso:
scp user@remote-machine:'something' ...
Na máquina remota, sshd
executará o shell de login de user
como:
that-shell -c 'scp -f something'
E é esse shell que expandirá os padrões lá.
O que isso significa é que a maneira como o padrão é expandido dependerá do shell de login que o usuário remoto está usando. Quando esse shell for bash
, isso também dependerá do que o usuário tem em ~/.bashrc
, ou o que existe em /etc/bash.bashrc
(por exemplo, bash-completion
chamado a partir de lá quando instalado, ativa globbing estendido no estilo ksh) já que, por algum motivo, bash
lê esses quando é chamado de ssh
(mesmo que não seja um shell interativo).
O que isso também significa é que se something
for, por exemplo, 'blah; rm -rf "$HOME"'
, isso terá conseqüências catastróficas.
Esse é um dos misdesigns de ssh
.
Se você quiser forçar um shell específico a expandir esse something
em uma lista de arquivos, use este truque:
LC_SCPFILES=something scp -o SendEnv=LC_SCPFILES "localhost:</dev/null
bash -O extglob -c 'exec scp -f -- \$LC_SCPFILES';exit" /dest/dir
Isso só funciona se o shell de login do usuário remoto for um shell Unix normal (pelo menos das famílias Bourne, csh, rc ou fish) e bash
estiver instalado lá e sshd
permitir a passagem de variáveis de ambiente cujo nome começa com LC_
.
O truque é que a maioria das sshd
deployments permite passar variáveis de ambiente cujo nome começa com LC_
(necessário para que um usuário espanhol possa receber mensagens de erro dos comandos remotos em espanhol (em vez do idioma padrão no sistema remoto) ou a linguagem do usuário remoto), por exemplo).
Então, aqui, passamos something
em uma variável de ambiente LC_SCPFILES
.
Na máquina remota, sshd
será executado:
that-shell -c 'scp -f </dev/null
bash -O extglob -c '\''exec scp -f -- $LC_SCPFILES'\'';exit'
Como seu stdin é redirecionado de /dev/null
, o primeiro scp
sairá imediatamente. Em seguida, iniciamos nosso shell de escolha ( bash
), com a opção extglob
e com a linha de comando exec scp -f -- $LC_SCPFILES
para interpretar.
Como bash
será executado em um processo filho de that-shell
(que aplicamos adicionando um ;exit
posteriormente), bash
não fornecerá o ~/.bashrc
, portanto, podemos esperar o comportamento padrão de bash
there (a menos que, de alguma forma, that-shell
, na inicialização, defina as variáveis de ambiente BASHOPTS
, SHELLOPTS
ou BASH_ENV
).
Como $LC_SCPFILES
não é citado, ele estará sujeito a divisão de palavras e geração de nome de arquivo (globbing), mas não a outras expansões de shells, e não fará coisas como rm -rf "$HOME"
apenas porque something
o contém.
Agora que nos certificamos de que o padrão é expandido em bash -O extglob
na máquina remota, podemos usá-lo para corresponder a um padrão que usa operadores glob estendidos (usando aqui uma função auxiliar):
safer_scp() (
file=$1; shift
export LC_SCPFILES="${file#*:}"
exec scp -o SendEnv=LC_SCPFILES "${file%%:*}:</dev/null
bash -O extglob -c 'exec scp -f -- \$LC_SCPFILES';exit" "$@"
)
safer_scp user@host:'file-name-version-+([0-9]).+([0-9]).+([0-9]).jar' .
O +([0-9])
, com extglob
em bash
, corresponde a um ou mais dígitos decimais. Portanto, o padrão acima corresponderia a file-name-version-1.2.3.jar
e file-name-version-12.123.1234.jar
, por exemplo.
(observe que safer_scp
acima supõe que o primeiro argumento é host:file-patterns
, coisas como safer_scp -r ...
ou safer_scp host1:f1 host2:f2 ...
não funcionam. Se você quiser usar -r
, o mais fácil é definir um safer_scp_r
function onde scp -f
acima é substituído por scp -r -f
).
Você pode desativar a divisão de palavras adicionando um IFS=;
antes do exec scp...
acima.
Observe também que estamos usando bash
aqui, pois ele está instalado em todos os sistemas GNU, mas se você sabe que zsh
está instalado no sistema remoto, você pode usar
zsh -o extendedglob -o globsust
em vez de se beneficiar do poder total de zsh
globbing (recursivo, qualificadores ...)
(adicione -o shwordsplit
se quiser também a divisão de palavras).
Por exemplo:
safer_scp() (
file=$1; shift
export LC_SCPFILES="${file#*:}"
exec scp -o SendEnv=LC_SCPFILES "${file%%:*}:</dev/null
zsh -o extendedglob -o globsubst -c 'exec scp -f -- \$LC_SCPFILES';exit" "$@"
)
safer_scp user@host:'file-name-version-<->(.<->)#.jar' .
Copia file-name-version-1.jar
e file-name-version-1.2.3.4.5.jar
( <x-y>
é qualquer número inteiro decimal de x
a y
, <->
é qualquer número inteiro decimal, #
é como o operador *
regexp (0 ou mais do átomo anterior))