我知道可以为单个命令/内置的范围设置自定义 IFS 值。有没有办法为单个语句设置自定义 IFS 值?显然不是,因为根据下面的尝试,全局 IFS 值会受到影响
#check environment IFS value, it is space-tab-newline
printf "%s" "$IFS" | od -bc
0000000 040 011 012
\t \n
0000003
#invoke built-in with custom IFS
IFS=$'\n' read -r -d '' -a arr <<< "$str"
#environment IFS value remains unchanged as seen below
printf "%s" "$IFS" | od -bc
0000000 040 011 012
\t \n
0000003
#now attempt to set IFS for a single statement
IFS=$'\n' a=($str)
#BUT environment IFS value is overwritten as seen below
printf "%s" "$IFS" | od -bc
0000000 012
\n
0000001
Run Code Online (Sandbox Code Playgroud)
Sté*_*las 49
在某些 shell 中(包括bash
):
IFS=: command eval 'p=($PATH)'
Run Code Online (Sandbox Code Playgroud)
(使用bash
,您可以command
在 sh/POSIX 仿真中省略if not)。但请注意,当使用不带引号的变量时,您通常还需要set -f
,并且在大多数 shell 中没有局部作用域。
使用 zsh,您可以执行以下操作:
(){ local IFS=:; p=($=PATH); }
Run Code Online (Sandbox Code Playgroud)
$=PATH
是强制分词,这在默认情况下未完成zsh
(也未完成变量扩展时的通配符,因此set -f
除非在 sh 仿真中,否则您不需要)。
但是,在 中zsh
,您宁愿使用$path
which 是一个绑定到的数组$PATH
,或者使用任意分隔符拆分:p=(${(s[:])PATH})
或者p=("${(s[:]@)PATH}")
保留空元素。
(){...}
(或function {...}
) 称为匿名函数,通常用于设置局部作用域。对于支持函数局部作用域的其他 shell,您可以执行类似的操作:
e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'
Run Code Online (Sandbox Code Playgroud)
要在 POSIX shell 中实现变量和选项的局部范围,您还可以使用https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh提供的函数。然后您可以将其用作:
. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'
Run Code Online (Sandbox Code Playgroud)
(顺便$PATH
说一句,除了zsh
在其他 shell 中,IFS 是字段分隔符,而不是字段分隔符,否则以上述方式拆分是无效的)。
IFS=$'\n' a=($str)
Run Code Online (Sandbox Code Playgroud)
只是两个作业,一个接一个就好了a=1 b=2
。
注释说明var=value cmd
:
在:
var=value cmd arg
Run Code Online (Sandbox Code Playgroud)
shell/path/to/cmd
在一个新进程中执行并传入cmd
and arg
inargv[]
和var=value
in envp[]
。这不是真正的变量赋值,而是更多地将环境变量传递给执行的命令。在 Bourne 或 Korn shell 中,使用set -k
,您甚至可以编写它cmd var=value arg
。
现在,这不适用于未执行的内置函数或函数。在 Bourne shell 中, in var=value some-builtin
,var
最终会在之后设置,就像 with 一样var=value
。这意味着例如var=value echo foo
(这是没有用的)的行为取决于是否echo
内置。
POSIX 和/或ksh
改变了这一点,因为 Bourne 行为只发生在称为special builtins 的一类内置函数中。eval
是一个特殊的内置read
函数,不是。对于非特殊内置,var=value builtin
套var
只为这使得它表现类似于当外部命令运行到内建的执行。
该command
命令可用于删除那些特殊内置函数的特殊属性。POSIX 忽略的是,对于和内置函数,这意味着 shell 必须实现一个变量堆栈(即使它没有指定或范围限制命令),因为你可以这样做:eval
.
local
typeset
a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a
Run Code Online (Sandbox Code Playgroud)
甚至:
a=1 command eval myfunction
Run Code Online (Sandbox Code Playgroud)
与myfunction
作为一个函数使用或设置$a
和潜在呼叫command eval
。
这是一个真正的俯瞰,因为ksh
(该规范主要是基于)没有实现它(和AT&Tksh
和zsh
仍然没有),但现在,除了这两个,大多数shell执行它。不同 shell 的行为有所不同,但在以下方面:
a=0; a=1 command eval a=2; echo "$a"
Run Code Online (Sandbox Code Playgroud)
尽管。local
在支持它的 shell 上使用是实现局部作用域的更可靠的方法。
msw*_*msw 16
来自 Kernighan 和 Pike 的“Unix 编程环境”的标准保存和恢复:
#!/bin/sh
old_IFS=$IFS
IFS="something_new"
some_program_or_builtin
IFS=${old_IFS}
Run Code Online (Sandbox Code Playgroud)
hel*_*hod 10
将您的脚本放入一个函数并调用该函数,并将命令行参数传递给它。由于 IFS 是本地定义的,因此对其的更改不会影响全局 IFS。
main() {
local IFS='/'
# the rest goes here
}
main "$@"
Run Code Online (Sandbox Code Playgroud)
小智 7
对于这个命令:
IFS=$'\n' a=($str)
Run Code Online (Sandbox Code Playgroud)
有一个替代解决方案:给第一个赋值 ( IFS=$'\n'
) 一个要执行的命令(一个函数):
$ split(){ a=( $str ); }
$ IFS=$'\n' split
Run Code Online (Sandbox Code Playgroud)
那会将 IFS 放入环境中调用 split,但不会保留在当前环境中。
这也避免了始终冒险使用 eval。
这个问题的片段:
IFS=$'\n' a=($str)
Run Code Online (Sandbox Code Playgroud)
被解释为从左到右计算的两个单独的全局变量赋值,等效于:
IFS=$'\n'; a=($str)
Run Code Online (Sandbox Code Playgroud)
或者
IFS=$'\n'
a=($str)
Run Code Online (Sandbox Code Playgroud)
这既解释了为什么IFS
修改了全局$str
变量,也解释了为什么使用 的新值将 的词拆分为数组元素IFS
。
您可能会想使用子外壳来限制IFS
修改的效果,如下所示:
str="value 0:value 1"
a=( old values )
( # Following code runs in a subshell
IFS=":"
a=($str)
printf 'Subshell IFS: %q\n' "${IFS}"
echo "Subshell: a[0]='${a[0]}' a[1]='${a[1]}'"
)
printf 'Parent IFS: %q\n' "${IFS}"
echo "Parent: a[0]='${a[0]}' a[1]='${a[1]}'"
Run Code Online (Sandbox Code Playgroud)
但是你很快就会注意到 的修改a
也仅限于子shell:
Subshell IFS: :
Subshell: a[0]='value 0' a[1]='value 1'
Parent IFS: $' \t\n'
Parent: a[0]='old' a[1]='values'
Run Code Online (Sandbox Code Playgroud)
接下来,您可能会想使用@msw之前的答案中的解决方案来保存/恢复 IFS ,或者尝试使用@helpermethod建议的local IFS
内部函数。但是很快,您就会发现自己遇到了各种各样的麻烦,尤其是如果您是一位需要对行为不端的调用脚本保持稳健的库作者:
IFS
最初未设置怎么办?set -u
(又名set -o nounset
)怎么办?IFS
通过 设为只读declare -r IFS
怎么办?trap
程序)怎么办?请不要保存/恢复 IFS。相反,坚持临时修改:
要将变量修改限制为单个命令、内置或函数调用,请使用IFS="value" command
.
要通过拆分特定字符(:
以下用作示例)来读入多个变量,请使用:
IFS=":" read -r var1 var2 <<< "$str"
Run Code Online (Sandbox Code Playgroud)要读入数组,请使用(执行此操作而不是array_var=( $str )
):
IFS=":" read -r -a array_var <<< "$str"
Run Code Online (Sandbox Code Playgroud)将修改变量的影响限制在子 shell 中。
输出以逗号分隔的数组元素:
(IFS=","; echo "${array[*]}")
Run Code Online (Sandbox Code Playgroud)要将其捕获到字符串中:
csv="$(IFS=","; echo "${array[*]}")"
Run Code Online (Sandbox Code Playgroud) 归档时间: |
|
查看次数: |
59223 次 |
最近记录: |