如何在Bourne Shell中合规存储与恢复IFS?含实践疑问
在Bash中,我们可以通过以下方式先存储输入字段分隔符(IFS),修改使用后再恢复:
${IFS+"false"} && unset oldifs || oldifs="$IFS" # 正确存储IFS IFS="error" # 根据需要修改并使用IFS ${oldifs+"false"} && unset IFS || IFS="$oldifs" # 恢复IFS
但该方法不符合POSIX标准,无法在Bourne Shell中运行。
尝试以下方法似乎可行:
$(echo ${IFS+"false"}) && unset oldifs || oldifs="$IFS" # 正确存储IFS IFS="error" # 根据需要修改并使用IFS $(echo ${oldifs+"false"}) && unset IFS || IFS="$oldifs" # 恢复IFS
请问该方法是否正确?此外,相比echo我更倾向于使用跨Shell一致性更好的printf,但当oldifs为空时,执行最后一行会抛出"printf: usage: printf format [arg ...]"错误,导致执行|| IFS="$oldifs"而非&& unset IFS,该如何解决?
回答
1. 尝试的方法是否正确?
该方法是正确的,完全符合POSIX标准,可在Bourne Shell中正常运行。
原Bash方法的问题在于直接将${IFS+"false"}作为命令执行——POSIX标准不允许将参数展开的结果直接当作命令(除非展开结果是合法的内置/外部命令名)。而通过$(echo ${IFS+"false"})的命令替换,会先将参数展开的结果通过echo输出,再将输出内容作为命令执行,这完全符合POSIX对命令执行的规范。
逻辑上和原方法完全一致:
- 当IFS存在(哪怕是空值),
${IFS+"false"}会展开为字符串false,echo输出false后执行该命令,返回非0值,进入||分支将当前IFS存入oldifs; - 当IFS不存在,
${IFS+"false"}展开为空,echo输出空字符串,执行空命令返回0值,进入&&分支执行unset oldifs。
2. 如何解决printf的报错问题?
报错的原因是当oldifs为空时,${oldifs+"false"}展开为空,直接执行$(printf ${oldifs+"false"})相当于调用printf时没有传入任何参数,违反了POSIX中printf必须至少指定格式字符串的语法要求。
解决方法是给printf指定一个明确的格式字符串,确保无论展开结果是否为空,printf都能接收到合法的参数:
# 存储IFS $(printf '%s' ${IFS+"false"}) && unset oldifs || oldifs="$IFS" # 修改并使用IFS IFS="error" # 恢复IFS $(printf '%s' ${oldifs+"false"}) && unset IFS || IFS="$oldifs"
这里的%s是POSIX标准的格式符,用于输出字符串:
- 当
${oldifs+"false"}展开为false时,printf '%s' false输出false,执行该命令返回非0,进入||分支恢复IFS; - 当
${oldifs+"false"}展开为空时,printf '%s'会输出空字符串(对应空参数),执行空命令返回0,进入&&分支执行unset IFS,完全符合预期逻辑且不会报错。
内容的提问来源于stack exchange,提问作者mabam

