Editar:
Nota importante antes de continuar lendo: desde a resposta de Kusalananda , minha pergunta agora é bastante inútil, pois ele apontou um bug em outro script que usei. Agora este bug foi corrigido, os erros abaixo que descrevi não ocorrem mais!
Existem dois problemas no seu código que fazem com que o valor em $extension seja expandido
O segundo problemaéa própria função
TL;DR
Eu estava preso quando comecei a escrever a pergunta, mas encontrei uma solução que funciona (usando shell noglob ), MAS ainda tenho uma pergunta de "seria possível escrever".
Tenho certeza que isso já foi respondido, mas me perco em centenas de tópicos sobre expansão do bash.
Aqui está o meu caso:
- Eu quero executar um script (digamos
ffmpeg
com o modo de opção-pattern_type glob -i
que aceita um regexp como*.JPG
) - tendo um argumento (vamos dizer
*.JPG
) - argumento vindo de uma variável (digamos
$extension
) - variável proveniente de parâmetros de script (digamos
$1
) - mas não quero que o shell se expanda
$extension
como padrão para um padrão como*.JPG
Observe que todas essas ações ocorrem em um script script.sh
.
Assim, o usuário poderia executar:
script.sh 'JPG' ; # where $1='JPG'
Nota: em script.sh
, eu concateno *.
e $1
para obter uma regexp mais final como*.JPG
Eu li (aqui https://stackoverflow.com/a/11456496/912046 ) que a expansão do shell ocorre antes de ffmpeg
receber argumentos ( ffmpeg
mesmo não sabe que a expansão do Shell ocorreu)
Tentei brincar com aspas, aspas duplas ou barras invertidas ${extension}
de diferentes maneiras, sem sucesso.
Aqui estão algumas tentativas:
ffmpeg ... "${extension}"
leva a: ffmpeg 1.jpg 2.jpg [...]
(expansão ocorre)
ffmpeg ... ${extension}
mesmo
ffmpeg ... '${extension}'
leva a nenhuma correspondência de arquivo devido ao padrão de pesquisa ${extension}
(o nome da variável é usado literalmente)
extension="\'${extension}\'" ; # wrapping the single quotes (preventing expansion) directly in variable
ffmpeg ... ${extension}
leva a nenhuma correspondência de arquivo devido ao padrão de pesquisa '*.jpg'
(incluindo aspas simples)
ffmpeg ... \"${extension}\"
igual, mas procurando por "*.jpg"
(incluindo aspas duplas)
Finalmente consegui usar a opção Shell noglob.
Aqui está o comando para curiosos:
set -f ; # disable glob (prevent expansion on '*.jpg')
ffmpeg -pattern_type glob -i ${extension} movie.mp4 ;
set +f ; # restore, but what if glob was already disabled? I should not restore it then?
Mas, para minha curiosidade, existe outra maneira de impedir a expansão de um argumento de script proveniente de uma variável que QUEREMOS ser resolvida? (assim, a maneira que eu sei de impedir a expansão (usando aspas simples) faz com que a variável não seja mais resolvida/substituída).
Algo como : (mas eu li que a concatenação de strings deve ser evitada por segurança, injeção)
ffmpeg '${extension}'
|| | 3
|| 2
| 1
Onde :
- 1: abriria aspas simples para o resultado final ser
ffmpeg '*.jpg'
- 2: injetaria e resolveria nossa variável
extension
para.jpg
- 3: fecharia aspas simples para
ffmpeg
receber argumento como se eu tivesse escrito manualmente'*.jpg'
Edit: após comentário sobre como a variável de extensão é atribuída:
local extension="${2}" ;
echo $extension ;
extension="${extension:=jpg}" ; # Default on "jpg" extension
# wrapp extension type in single quotes + prefix with "*."
extension="*.${extension}" ;
Então, usando-o com:
runprintcommand ffmpeg -loglevel verbose -pattern_type glob -i ${extension} "$movieName" ;
Onde a runprintcommand
função/script sendo:
function runprintcommand() {
echo "Command to run: (Note: '\\' escape character may not be printed)" ;
echo "$*" ;
$* ;
}
Exemplo:
Existem dois problemas no seu código que fazem com que o valor
$extension
seja expandido para o padrão glob que contém e também faz com que o padrão glob seja comparado com os nomes dos ficheiros prematuramente (você deseja entregá-lo como está paraffmpeg -i
o qual faz a sua própria expansão glob internamente).A primeira é sua chamada para sua função:
Aqui,
${extension}
não está entre aspas, então você definitivamente obteria (divisão de palavras e) globbing de nome de arquivo acontecendo em seu valor.A segunda questão é a própria função:
Aqui, você usa
$*
unquoted, que novamente (dividir o valor em palavras e depois) expandir o glob, mesmo se você aspas duplas${extension}
na chamada para a função.Em vez de um bare
$*
, use"$@"
(com aspas duplas). Isso se expandiria para os parâmetros posicionais cotados individualmente da função.Esta é a diferença entre
"$@"
e"$*"
:"$*"
é uma única string com aspas duplas . Isso geralmente não pode ser usado para executar um comando com argumentos."$@"
é uma lista de palavras entre aspas duplas . Isso pode ser usado para executar um comando com argumentos.Como encontrei uma sintaxe alternativa para fazer isso, escrevo aqui.
Ao usar um sinalizador para desabilitar o globbing do Shell ( http://tldp.org/LDP/abs/html/globbingref.html ), ele impede a expansão:
A maneira de parar o shell de globbing é citar a expansão variável. (Ele também interrompe a divisão de palavras, que geralmente também é o que você deseja). Isso não impede que o próprio comando processe padrões semelhantes a glob, é claro.
ffmpeg
faz isso para-i
, e assim faz, por exemplofind -name
. Usando este último como exemplo:(A linha que começa com a
+
vem deset -x
e mostra o comando que o shell realmente executou.)Embora você também possa parar de globbing com
set -f
, isso não ajuda na divisão de palavras. Se a variável contivesse espaços em branco, a expansão sem aspas seria interrompida, mesmo comset -f
.