Em algumas conchas (incluindo bash
):
IFS=: command eval 'p=($PATH)'
(com bash
, você pode omitir a command
se não estiver na emulação sh / POSIX). Mas lembre-se de que, ao usar variáveis não citadas, você geralmente também precisa set -f
, e não há escopo local para isso na maioria dos shells.
Com o zsh, você pode fazer:
(){ local IFS=:; p=($=PATH); }
$=PATH
é forçar a divisão de palavras que não é feita por padrão em zsh
(globbing na expansão de variáveis também não é feito, então você não precisa, a set -f
menos que seja em emulação de sh).
(){...}
(ou function {...}
) são chamadas de funções anônimas e geralmente são usadas para definir um escopo local. com outros shells que oferecem suporte ao escopo local em funções, você pode fazer algo semelhante com:
e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'
Para implementar um escopo local para variáveis e opções nos shells POSIX, você também pode usar as funções fornecidas em https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh . Então você pode usá-lo como:
. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'
(a propósito, é inválido dividir $PATH
dessa maneira acima, exceto zsh
em outros shells, o IFS é delimitador de campo, não separador de campo).
IFS=$'\n' a=($str)
São apenas duas tarefas, uma após a outra a=1 b=2
.
Uma nota explicativa sobre var=value cmd
:
Em:
var=value cmd arg
Os executa shell /path/to/cmd
em um novo processo e passes cmd
e arg
em argv[]
e var=value
em envp[]
. Isso não é realmente uma atribuição de variável, mas mais variáveis de ambiente de passagem para o comando executado . No shell Bourne ou Korn, com set -k
, você pode até escrever cmd var=value arg
.
Agora, isso não se aplica a funções internas ou funções que não são executadas . No shell Bourne, em var=value some-builtin
, var
acaba sendo definido posteriormente, assim como var=value
sozinho. Isso significa, por exemplo, que o comportamento de var=value echo foo
(que não é útil) varia dependendo se echo
está embutido ou não.
O POSIX e / ou ksh
alterou o fato de que o comportamento Bourne ocorre apenas para uma categoria de componentes internos chamados componentes especiais . eval
é um builtin especial, read
não é. Para embutidos não especiais, var=value builtin
define var
apenas para a execução do embutido, o que o comporta de maneira semelhante a quando um comando externo está sendo executado.
O command
comando pode ser usado para remover o especial atributo desses builtins especiais . O que o POSIX ignorou é que, para os eval
e .
builtins, isso significaria que os shells precisariam implementar uma pilha variável (mesmo que não especifique os comandos de limitação de escopo local
ou typeset
), porque você poderia fazer:
a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a
Ou até:
a=1 command eval myfunction
com myfunction
sendo uma função usando ou a criação $a
e potencialmente chamando command eval
.
Isso foi realmente um esquecimento, porque ksh
(no qual a especificação é baseada principalmente) não a implementou (e a AT&T ksh
e zsh
ainda não o fez), mas hoje em dia, exceto os dois, a maioria das conchas o implementa. O comportamento varia entre as conchas, embora em coisas como:
a=0; a=1 command eval a=2; echo "$a"
Apesar. O uso local
de shells compatíveis é uma maneira mais confiável de implementar o escopo local.
IFS=: command eval …
defineIFS
apenas a duração doeval
, conforme exigido pelo POSIX, no hífen, pdksh e bash, mas não no ksh 93u. É incomum ver o ksh ser o único fora de conformidade.Salvar e restaurar padrão extraído de "The Unix Programming Environment", de Kernighan e Pike:
fonte
$IFS
corretamente se não estava definido anteriormente.$'\t\n'' '
, conforme explicado aqui: wiki.bash-hackers.org/syntax/expansion/…$' \t\n'
. o espaço precisa ser o primeiro a ser usado"$*"
. Observe que é o mesmo em todas as conchas do tipo Bourne.Coloque seu script em uma função e chame essa função passando os argumentos da linha de comando para ele. Como o IFS é definido como local, as alterações nele não afetam o IFS global.
fonte
Para este comando:
Existe uma solução alternativa: dar à primeira atribuição (
IFS=$'\n'
) um comando para executar (uma função):Isso colocará o IFS no ambiente para chamar divisão, mas não será retido no ambiente atual.
Isso também evita o uso sempre arriscado de avaliação.
fonte
$IFS
definido$'\n'
como posteriormente, conforme exigido pelo POSIX.A resposta proposta de @helpermethod é certamente uma abordagem interessante. Mas também é uma armadilha, porque no BASH o escopo da variável local se estende do chamador à função chamada. Portanto, configurar o IFS em main () resultará na persistência desse valor para funções chamadas de main (). Aqui está um exemplo:
E a saída ...
Se o IFS declarado em main () ainda não estivesse no escopo em func (), a matriz não teria sido analisada corretamente em func () B. Remova o comentário da primeira linha em func () e você obtém esta saída:
Qual é o que você deve obter se o IFS estiver fora do escopo.
Uma solução muito melhor, IMHO, é renunciar à mudança ou confiar no IFS em nível global / local. Em vez disso, crie uma nova concha e mexa com o IFS lá. Por exemplo, se você chamar func () em main () da seguinte maneira, passando a matriz como uma sequência com um separador de campo barra invertida:
... essa alteração no IFS não será refletida em func (). A matriz será passada como uma sequência:
... mas dentro de func () o IFS ainda será "/" (conforme definido em main ()), a menos que seja alterado localmente em func ().
Mais informações sobre o isolamento de alterações no IFS podem ser visualizadas nos seguintes links:
Como faço para converter uma variável do array bash em uma string delimitada por novas linhas?
Cadeia de caracteres de basquete para matriz com IFS
Dicas e sugestões para programação geral de scripts de shell - Consulte "OBSERVAÇÃO do uso de subcascas ..."
fonte
IFS=$'\n' declare -a astr=(...)
perfeito obrigado!Este trecho da pergunta:
é interpretado como duas atribuições de variáveis globais separadas avaliadas da esquerda para a direita e é equivalente a:
ou
Isso explica por que o global
IFS
foi modificado e por que a divisão da palavra$str
em elementos da matriz foi realizada usando o novo valor deIFS
.Você pode ficar tentado a usar um subshell para limitar o efeito da
IFS
modificação assim:mas você notará rapidamente que a modificação de
a
também está limitada ao subshell:Em seguida, você seria tentado a salvar / restaurar o IFS usando a solução desta resposta anterior por @msw ou a tentar usar
local IFS
uma função interna, conforme sugerido por @helpermethod. Mas, em breve, você perceberá que está com todo tipo de problemas, especialmente se você é um autor de uma biblioteca que precisa ser robusto contra o mau comportamento de scripts de chamada:IFS
foi inicialmente não definido?set -u
(akaset -o nounset
)?IFS
fosse feito somente leitura viadeclare -r IFS
?trap
manipulador`)?Por favor, não salve / restaure o IFS. Em vez disso, atenha-se a modificações temporárias:
Para limitar a modificação da variável a um único comando, chamada interna ou chamada de função, use
IFS="value" command
.Para ler várias variáveis dividindo um caractere específico (
:
usado abaixo como exemplo), use:Para ler em uma matriz, use (faça isso em vez de
array_var=( $str )
):Limite os efeitos da modificação da variável em um subshell.
Para gerar os elementos de uma matriz separados por vírgula:
Para capturar isso em uma sequência:
fonte
A solução mais direta é tirar uma cópia do original
$IFS
, como em, por exemplo, a resposta do msw. No entanto, esta solução não distingue entre um conjuntoIFS
e umIFS
conjunto igual à cadeia vazia, o que é importante para muitos aplicativos. Aqui está uma solução mais geral que captura essa distinção:fonte