En algunas conchas (incluidas bash
):
IFS=: command eval 'p=($PATH)'
(con bash
, puede omitir command
si no está en la emulación sh / POSIX). Pero tenga en cuenta que cuando usa variables sin comillas, generalmente también necesita hacerlo set -f
, y no hay un alcance local para eso en la mayoría de los shells.
Con zsh, puedes hacer:
(){ local IFS=:; p=($=PATH); }
$=PATH
es forzar la división de palabras que no se realiza de forma predeterminada en zsh
( no se hace globbing en la expansión de variables, por lo que no es necesario a set -f
menos que sea en emulación sh).
(){...}
(o function {...}
) se denominan funciones anónimas y generalmente se utilizan para establecer un ámbito local. con otros shells que admiten el alcance local en funciones, podría hacer algo similar con:
e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'
Para implementar un ámbito local para variables y opciones en shells POSIX, también puede usar las funciones proporcionadas en https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh . Entonces puedes usarlo como:
. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'
(por cierto, no es válido dividir de $PATH
esa manera arriba excepto en zsh
como en otros shells, IFS es delimitador de campo, no separador de campo).
IFS=$'\n' a=($str)
Son solo dos tareas, una tras otra igual a=1 b=2
.
Una nota de explicación sobre var=value cmd
:
En:
var=value cmd arg
Los ejecuta shell /path/to/cmd
en un nuevo proceso y pases cmd
y arg
en argv[]
y var=value
en envp[]
. Eso no es realmente una asignación de variables, sino más variables de entorno que pasan al comando ejecutado . En el shell Bourne o Korn, con set -k
, incluso puedes escribirlo cmd var=value arg
.
Ahora, eso no se aplica a las funciones o funciones incorporadas que no se ejecutan . En el shell Bourne, en var=value some-builtin
, var
termina siendo establecido después, al igual que con var=value
solo. Eso significa, por ejemplo, que el comportamiento de var=value echo foo
(que no es útil) varía dependiendo de si echo
está incorporado o no.
POSIX y / o ksh
cambió eso en el sentido de que el comportamiento de Bourne solo ocurre para una categoría de incorporados llamados incorporados especiales . eval
es una construcción especial, read
no lo es. Para el builtin no especial, se var=value builtin
establece var
solo para la ejecución del builtin que hace que se comporte de manera similar a cuando se ejecuta un comando externo.
El command
comando se puede utilizar para eliminar el atributo especial de esas incorporaciones especiales . Lo POSIX se pasa por alto es que aunque para los eval
y .
las órdenes internas, eso significaría que las conchas tendría que implementar una pila variable (aunque no especifica los local
o typeset
alcance comandos de limitación), ya que podría hacer:
a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a
O incluso:
a=1 command eval myfunction
con myfunction
ser una función usando o configurando $a
y potencialmente llamando command eval
.
Eso fue realmente un descuido porque ksh
(en el que se basa principalmente la especificación) no lo implementó (y AT&T ksh
y zsh
aún no lo hace), pero hoy en día, excepto esos dos, la mayoría de los shells lo implementan. El comportamiento varía entre los caparazones, aunque en cosas como:
a=0; a=1 command eval a=2; echo "$a"
aunque. El uso local
de shells que lo soportan es una forma más confiable de implementar el alcance local.
IFS=: command eval …
estableceIFS
solo para la duración deeval
, según lo dispuesto por POSIX, en guión, pdksh y bash, pero no en ksh 93u. Es inusual ver a ksh como un extraño que no cumple.Guardar y restaurar estándar tomado de "El entorno de programación Unix" por Kernighan y Pike:
fuente
$IFS
correctamente si no se configuró previamente.$'\t\n'' '
como se explica aquí: wiki.bash-hackers.org/syntax/expansion/…$' \t\n'
. el espacio tiene que ser el primero ya que se usa para eso"$*"
. Tenga en cuenta que es lo mismo en todos los shells tipo Bourne.Ponga su script en una función e invoque esa función pasándole los argumentos de la línea de comandos. Como IFS se define localmente, los cambios no afectan al IFS global.
fuente
Para este comando:
Hay una solución alternativa: dar a la primera asignación (
IFS=$'\n'
) un comando para ejecutar (una función):Eso pondrá a IFS en el entorno para dividir las llamadas, pero no se mantendrá en el entorno actual.
Esto también evita el uso siempre riesgoso de eval.
fuente
$IFS
configurado$'\n'
como lo requiere POSIX.La respuesta propuesta de @helpermethod es ciertamente un enfoque interesante. Pero también es una trampa porque en BASH el alcance de la variable local se extiende desde el llamador hasta la función llamada. Por lo tanto, al establecer IFS en main (), el valor persistirá en las funciones llamadas desde main (). Aquí hay un ejemplo:
Y la salida ...
Si IFS declarado en main () no estuviera todavía en alcance en func (), entonces la matriz no se habría analizado correctamente en func () B. Elimine el comentario de la primera línea en func () y obtendrá esta salida:
Que es lo que debería obtener si IFS hubiera salido del alcance.
Una solución mucho mejor en mi humilde opinión, es renunciar a cambiar o confiar en IFS a nivel global / local. En cambio, genera un nuevo caparazón y juega con IFS allí. Por ejemplo, si tuviera que llamar a func () en main () de la siguiente manera, pasando la matriz como una cadena con un separador de campo de barra invertida:
... ese cambio a IFS no se reflejará en func (). La matriz se pasará como una cadena:
... pero dentro de func () el IFS seguirá siendo "/" (como se establece en main ()) a menos que se cambie localmente en func ().
Se puede ver más información sobre cómo aislar cambios en IFS en los siguientes enlaces:
¿Cómo convierto una variable de matriz bash en una cadena delimitada con líneas nuevas?
Bash string a array con IFS
Consejos y sugerencias para la programación general de scripts de shell: consulte "NOTA el uso de sub-shells ..."
fuente
IFS=$'\n' declare -a astr=(...)
perfecto gracias!Este fragmento de la pregunta:
se interpreta como dos asignaciones de variables globales separadas evaluadas de izquierda a derecha, y es equivalente a:
o
Esto explica por qué
IFS
se modificó el global y por qué la división de palabras$str
en elementos de matriz se realizó utilizando el nuevo valor deIFS
.Es posible que tenga la tentación de usar una subshell para limitar el efecto de la
IFS
modificación de esta manera:pero notará rápidamente que la modificación de
a
también está limitada a la subshell:A continuación, estaría tentado a guardar / restaurar IFS utilizando la solución de esta respuesta anterior de @msw o intentar usar
local IFS
una función interna como lo sugiere @helpermethod. Pero muy pronto, te das cuenta de que estás en todo tipo de problemas, especialmente si eres un autor de la biblioteca que necesita ser robusto contra comportamientos invocadores que se comportan mal:IFS
inicialmente no se configuró?set -u
(akaset -o nounset
)?IFS
se hizo solo lectura a través dedeclare -r IFS
?trap
sucede si necesito el mecanismo de guardar / restaurar para trabajar con recursividad y / o ejecución asincrónica (como un controlador ')?Por favor no guarde / restaure IFS. En cambio, quédese con las modificaciones temporales:
Para limitar la modificación de la variable a un solo comando, invocación incorporada o de función, use
IFS="value" command
.Para leer en múltiples variables dividiendo en un carácter específico (
:
usado a continuación como ejemplo), use:Para leer en una matriz, use (haga esto en lugar de
array_var=( $str )
):Limite los efectos de modificar la variable a una subshell.
Para generar elementos de una matriz separados por comas:
Para capturar eso en una cadena:
fuente
La solución más directa es tomar una copia del original
$IFS
, como en, por ejemplo, la respuesta de msw. Sin embargo, esta solución no distingue entre un desarmadoIFS
y unIFS
conjunto igual a la cadena vacía, lo cual es importante para muchas aplicaciones. Aquí hay una solución más general que captura esta distinción:fuente