(为简单起见,我假设要读取的文件是第一个参数 - $1
。)
我可以做我想做的事外在地和:
tempfile=$(mktemp)
awk '/^#/ {next}; NF == 0 {next}; {print}' "$1" > $tempfile
while read var1 var2 var3 var4 < $tempfile; do
# stuff with var1, etc.
done
awk
然而,每次解析配置文件时都需要调用似乎很荒谬。有没有办法让read
忽略文件中的注释行或仅空白行,没有外部二进制文件/潜在的性能问题?
到目前为止的答案非常有帮助!澄清一下,我不想使用临时文件,但我做想要读取配置从文件中,不是来自标准输入。我很清楚,在调用脚本时可以使用输入重定向,但由于各种原因,这在我的情况下不起作用。
我想对要读取的输入进行软编码,例如:
configfile="/opt/myconfigfile.txt"
[ $# -gt 0 ] && [ -r "$1" ] && configfile="$1"
while read var1 var2 var3 var4 < "$configfile" ; do
...
configfile
但是当我尝试这个时,它只是一遍又一遍地读取第一行,直到我终止该进程。
也许这应该是它自己的问题......但这可能是我正在做的事情的一行变化。我的错误在哪里?
答案1
您不需要临时文件来执行此操作,并且 sed(或 awk)在注释处理方面比 shell case 语句灵活得多。
例如:
configfile='/opt/myconfigfile.txt'
[ $# -gt 0 ] && [ -r "$1" ] && configfile="$1"
sed -e 's/[[:space:]]*#.*// ; /^[[:space:]]*$/d' "$configfile" |
while read var1 var2 var3 var4; do
# stuff with var1, etc.
done
# Note: var1 etc are not available to the script at this
# point. They are only available in the sub-shell running
# the while loop, and go away when that sub-shell ends.
这会删除注释(带或不带前导空格)并从输入中删除空行,然后再将其输送到 while 循环中。它单独处理行上的注释以及附加到行尾的注释:
# full-line comment
# var1 var2 var3 var4
abc 123 xyz def # comment here
像这样的呼叫sed
或awk
任务并不“荒谬”,而是完全正常的。这就是这些工具的用途。至于性能,我敢打赌,除了非常小的输入文件之外,该sed
版本会快得多。管道传输sed
有一些启动开销,但运行速度非常快,而 shell 则很慢。
2022年5月3日更新:
请注意,当 while 循环结束时,在 while read 循环中设置的变量(var1、var2、var3 等)将“超出范围”。只能在 while 循环内部使用。 while 循环正在子 shell 中运行,因为配置文件通过管道传输到其中。当该子 shell 死亡时,它的环境和子进程也会随之消失不能改变其父进程的环境。
如果您希望变量在 while 循环后保留其值,则需要避免使用管道。例如,使用输入重定向 ( <
) 和流程替代( <(...)
):
while read var1 var2 var3 var4; do
# stuff with var1, etc.
done < <(sed -e 's/[[:space:]]*#.*// ; /^[[:space:]]*$/d' "$configfile")
# remainder of script can use var1 etc if and as needed.
使用此进程替换版本,while 循环在父 shell 中运行,并且sed
脚本作为子进程运行(其输出重定向到 while 循环)。 sed 及其环境在完成后消失,而运行 while 循环的 shell 保留循环创建/更改的变量。
答案2
这是有效的,因为read
会破坏空白 (IFS) 上的所有内容,因此如果 var1 为空或以“#”开头,则跳过它。
while read var1 var2 var3 var4; do
case $var1 in
''|\#*) continue ;; # skip blank lines and lines starting with #
esac
echo "var1: '$var1'"
# stuff with var1, etc.
done < "${1:-default_config_file}"
然后输入必须重定向到循环而不是while
命令列表。如果不为空则扩展"${1:-default_config_file}"
为第一个命令行参数,否则扩展为default_config_file
您也可以在默认值字符串中使用变量扩展等。
因为您对最小化预处理感兴趣,所以我认为这是等效的,但也删除了所有注释:
while read line; do
echo "${line%%#*}" | {
read var1 var2 var3 var4
[ -z "$var1" ] && continue
# stuff with var1, etc.
for i in 1 2 3 4; do eval echo "\"var$i: \$var$i\""; done #debug only!
}
done < "${1:-default_config_file}"
这使用了 shell 参数扩展子字符串处理功能。 扩展为除第一个和删除后的所有内容之外${line%%#*}
的原始值。将其加载并照常继续。测试缩短了,因为我们现在只需要检查空字符串,而不是。line
#
var1-4
continue
#
答案3
您可以在不创建临时文件的情况下执行此操作。 grep 命令将过滤空行和注释行。
while read var1 var2 var3; do
echo $var1
echo $var2
echo $var3
echo "etc..."
done < <(grep -v "^#\|^$" /opt/myconfigfile.txt)