En algunas conchas (incluidas bash):
IFS=: command eval 'p=($PATH)'
(con bash, puede omitir commandsi 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); }
$=PATHes 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 -fmenos 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 $PATHesa manera arriba excepto en zshcomo 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/cmden un nuevo proceso y pases cmdy argen argv[]y var=valueen 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, vartermina siendo establecido después, al igual que con var=valuesolo. Eso significa, por ejemplo, que el comportamiento de var=value echo foo(que no es útil) varía dependiendo de si echoestá incorporado o no.
POSIX y / o kshcambió eso en el sentido de que el comportamiento de Bourne solo ocurre para una categoría de incorporados llamados incorporados especiales . evales una construcción especial, readno lo es. Para el builtin no especial, se var=value builtinestablece varsolo para la ejecución del builtin que hace que se comporte de manera similar a cuando se ejecuta un comando externo.
El commandcomando se puede utilizar para eliminar el atributo especial de esas incorporaciones especiales . Lo POSIX se pasa por alto es que aunque para los evaly .las órdenes internas, eso significaría que las conchas tendría que implementar una pila variable (aunque no especifica los localo typesetalcance 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 myfunctionser una función usando o configurando $ay 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 kshy zshaú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 localde shells que lo soportan es una forma más confiable de implementar el alcance local.
IFS=: command eval …estableceIFSsolo 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
$IFScorrectamente 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
$IFSconfigurado$'\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é
IFSse modificó el global y por qué la división de palabras$stren 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
IFSmodificación de esta manera:pero notará rápidamente que la modificación de
atambié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 IFSuna 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:IFSinicialmente no se configuró?set -u(akaset -o nounset)?IFSse hizo solo lectura a través dedeclare -r IFS?trapsucede 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 desarmadoIFSy unIFSconjunto 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