Uma resposta comoessefaz um bom trabalho explicando como controlar os passestodosvariáveis até um comando.
Eu queria explorar como fazer isso por argumento. Observe (isso foi testado em zsh
):
$ program() { echo 1: $1 2: $2 3: $3; }
$ run() { program "$@"; }
$ run2() { echo `run $1`; }
$ run2 'a b' c
1: a b 2: 3:
Eu quero uma maneira de passar um primeiro argumento que é uma string que contém espaços e ter esse $@
estilo emendado em outro comando. por exemplo, run2 "a b" c
deve produzir1: a 2: b 3:
Neste ponto, resolvi meu problema imediato porque, embora meu código de teste seja quebrado quando o testei no zsh, uma vez implementado em um script bash real, ele funciona.
Isso indica que talvez isso dependa de algumas complexidades no tratamento de strings e argumentos que não são "seguras". Portanto, este é mais um pedido de comentário sobre uma maneira mais robusta de alcançar esse comportamento de forma confiável.
Responder1
A diferença entre bash e zsh que importa aqui está na forma como run2
chama run
, especificamente no efeito de deixar $1
sem aspas.
- No zsh,
run $1
aplica o operador “remove-if-empty” a$1
, ou seja, chamarun
com o primeiro argumento que foi passado pararun2
, exceto que se o primeiro argumento pararun2
estava vazio (ou serun2
foi chamado sem argumento), entãorun
é chamado sem argumento. - Em outros shells estilo Bourne, como o bash,
run $1
aplica o operador “split+glob” a$1
, ou seja, divide o primeiro argumento emrun2
pedaços separados por espaços em branco¹, interpreta cada parte como um padrão curinga² e substitui cada padrão curinga que corresponde a um ou mais arquivo pela lista de correspondências.
Assim, run2 'a b' c
chama run
com o argumento a b
em zsh (o argumento é passado inalterado), mas chama run
com os dois argumentos a
e b
em bash (dividido em partes delimitadas por espaços em branco).
Eu quero uma maneira de passar um primeiro argumento que é uma string que contém espaços e ter esse
$@
estilo emendado em outro comando. por exemplo,run2 "a b" c
deve produzir1: a 2: b 3:
Sua descrição e seu exemplo dizem coisas diferentes. Se você quiser passar o primeiro argumento para o outro comando, use run "$1"
para garantir que o argumento não seja dividido. Passar argumentos inalterados é o objetivo do "$@"
.
Parece que o que você realmente quer fazer é dividir o primeiro argumento run2
em pedaços delimitados por espaços em branco. No bash, você pode fazer isso desativando a expansão curinga e depois (supondo que IFS
o padrão não seja alterado) usando uma expansão sem aspas.
run2 () ( set -f; run $1; )
( echo "$(somecommand)"
é essencialmente equivalente a executar somecommand
em um subshell, e parece que é isso que você quis dizer, em vez de echo $(somecommand)
aplicar split+glob na saída do comando, então removi a substituição redundante de comando de eco.)
No zsh, você pode usar o =
caractere em uma substituição de parâmetro para realizar a divisão mundial (e sem globbing) no valor.
run2 () { run $=1; }
A sintaxe do zsh não é compatível com a do sh simples. Se você deseja obter um script sh do zsh, você pode usar a emulate
construção:
emulate sh -c '. myscript.sh'
Use emulate ksh
para emular (alguns recursos do) ksh. Isso não emula todos os recursos do bash, mas permite usar arrays.
¹ De forma mais geral, com base no valor de IFS
.
² A menos que tenha sido desativado com set -f
.
Responder2
Bem-vindo ao mundo das citações e das citações surpresas.
O principal problema é que zsh
não é dividido em caracteres IFS por padrão.
Nisso: é diferente de todas as outras conchas.
Para testar como as cotações mudam a maneira como os shells funcionam, precisamos de um código que teste versões entre aspas e sem aspas do seu código.
Seu código (adicionando algumas variáveis):
program() { printf '%02d-1: %6s 2: %6s 3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@" ; }
run1() { echo "`runa $1 $2 $3 `"; }
run1 'a b' c
Deixe-me expandir os detalhes.
Vamos supor que você crie um sh
link local apontando para onde zsh
mora:
ln -s "/usr/bin/zsh" ./sh
E também, vamos supor que você copie o script a seguir para um so
arquivo.
Um script repetindo cada função com variáveis entre aspas e sem aspas:
program() { printf '%02d-1: %6s 2: %6s 3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@"; }
runb() { program $@ ; }
run1() { echo "`runa "$1" "$2" "$3"`"; }
run2() { echo "`runb "$1" "$2" "$3"`"; }
run3() { echo "`runa $1 $2 $3`"; }
run4() { echo "`runb $1 $2 $3`"; }
for i in `seq 4`; do
run"$i" 'a b' c
done
Então, na execução, imprimimos isto:
# Any shell (except zsh) results.
01-1: a b 2: c 3:
02-1: a 2: b 3: c
03-1: a 2: b 3: c
04-1: a 2: b 3: c
Somente a primeira execução (run1), onde tudo está entre aspas, permanece 'a b'
unida.
No entanto, zsh age como se tudo tivesse sido citado o tempo todo:
# ZSH results.
01-1: a b 2: c 3:
02-1: a b 2: c 3:
03-1: a b 2: c 3:
04-1: a b 2: c 3:
zsh na emulação.
Supõe-se que zsh
emulará shells mais antigos se for chamado como sh
ou ksh
.
Mas na prática isso nem sempre é verdade:
$ ./sh ./so # emulated sh
01-1: a b 2: c 3:
02-1: a b 2: c 3:
03-1: a 2: b 3: c
04-1: a 2: b 3: c
A segunda linha é diferente da segunda linha de qualquer outro shell.
É uma boa ideialeia as respostas para esta pergunta