Como "expandir" uma variável bash (o código incluído funciona para o bash mas não para o zsh)

0

Uma resposta como isso faz um bom trabalho ao explicar como controlar as variáveis todas através de um comando.

Eu queria explorar como fazer isso em uma base 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 seja uma string que tenha espaços, e que tenha emendado $@ style em outro comando. por exemplo. run2 "a b" c deve produzir 1: a 2: b 3:

Neste ponto, resolvi meu problema imediato porque, embora meu código de teste seja interrompido quando eu testei em zsh, uma vez implementado em um script real, ele funciona.

Isso indica que talvez isso dependa de algumas complexidades no processamento de strings e argumentos que não são "seguros". Portanto, este é mais um pedido de comentário sobre uma maneira mais robusta de alcançar esse comportamento de maneira confiável.

    
por Steven Lu 23.09.2016 / 17:33

2 respostas

3

A diferença entre o bash e o zsh que importa aqui está no caminho run2 calls run , especificamente o efeito de deixar $1 sem aspas.

  • Em zsh, run $1 aplica o operador "remove-if-empty" a $1 , ou seja, chama run com o primeiro argumento que foi passado para run2 , exceto se o primeiro argumento for run2 estava vazio (ou se run2 foi chamado sem argumento), então run é chamado sem argumento.
  • Em outros shells no estilo Bourne, como bash, run $1 aplica o operador “split + glob” a $1 , ou seja, ele divide o primeiro argumento em run2 em partes separadas por espaços em branco¹, interpreta cada parte como um padrão de caractere curinga² e substitui cada padrão de caractere curinga que corresponde a um ou mais arquivos 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 no bash (dividido em espaços em branco- peças delimitadas).

I want a way to pass a first arg which is a string that has spaces in it, and have that spliced $@style into another command. e.g. run2 "a b" c should produce 1: 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 ponto inteiro de "$@" .

Parece que o que você realmente quer fazer é dividir o primeiro argumento para run2 em partes delimitadas por espaço em branco. No bash, você pode fazer isso desativando a expansão de curinga e, em seguida, (assumindo que IFS não foi alterado do padrão) 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 é o que você quis dizer em vez de echo $(somecommand) que aplica split + glob na saída do comando, então eu removi o redundante echo-command-substitution.)

No zsh, você pode usar o caractere = em uma substituição de parâmetro para realizar a divisão do mundo (e não globbing) no valor.

run2 () { run $=1; }

A sintaxe do zsh não é compatível com a da sh simples. Se você deseja criar um script sh a partir do zsh, você pode usar o emulate builting:

emulate sh -c '. myscript.sh'

Use emulate ksh para emular (alguns recursos de) ksh. Isso não emula todos os recursos do bash, mas permite usar matrizes.

¹ Mais geralmente, com base no valor de IFS .
² A menos que isto tenha sido desativado com set -f .

    
por 25.09.2016 / 01:19
0

Bem-vindo ao mundo das citações e citações de surpresas.

A questão principal é que zsh não se divide em caracteres IFS por padrão.
Nisso: é diferente de todas as outras conchas.

Para testar o modo como as citações mudam a maneira como os shells funcionam, precisamos de código que teste as versões citadas e não citadas de 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 link sh local apontando para onde zsh reside:

ln -s "/usr/bin/zsh" ./sh

E, também, vamos supor que você copie o seguinte script para um arquivo so .

Um script repetindo cada função com variáveis citadas e não citadas:

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, em execução, nós imprimimos isso:

# 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

Apenas a primeira execução (run1), onde tudo está entre aspas, mantém 'a b' unida.

No entanto, o 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 antigas se 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 para qualquer outro shell.

É uma boa ideia ler as respostas para essa pergunta

    
por 23.09.2016 / 22:20

Tags