有时候,我们可能需要在 Shell 中维护一个KV结构的数组,也就是类似于 List<Map<String,Stirng>>
这样的数据结构,但是这在 Shell 中并不容易实现,一些联合使用 Shell 的 Indexed Arrays 和 Associative Arrays 进行模拟的方案虽然可行,但其实使用起来还是很别扭的,此时,最自然做法是把这些值抽离到单独的 CSV 文件中维护,然后用 Shell 循环读取每一行并对变量进行赋值。这里插一句题外话,这个需求使用 Json 文件同样可以实现,配合强大的 jq 命令可以轻松提取各种复杂的嵌套结构。不过这里的数据结构非常简单,使用 csv 储存和读写都会简洁许多,两种风格可以视需求和个人喜好自定定夺。
我们以这样一个 CSV 文件为例:
containers-per-node,map-mem,map-vcores,reduce-mem,reduce-vcores,am-mem,am-vcores,mappers
4,15360,8,15360,8,15360,8,127
8,7680,4,7680,4,7680,4,255
12,5120,3,5120,4,5120,4,383
16,3840,2,7680,4,7680,4,510
下面的 Shell 脚本演示了如何读取每一行内容,并将相应的字段赋值给对应变量:
IFS=,
row=0
cat above-csv-file.csv | while read -r CONTAINERS_PER_NODE MAP_MEM MAP_VCORES REDUCE_MEM REDUCE_VCORES AM_MEM AM_VCORES MAPPERS; do
# skip first header line
if [[ $row -eq 0 ]]; then
row=$((row+1))
continue
fi
echo "mapreduce.map.memory.mb = $MAP_MEM"
echo "mapreduce.map.cpu.vcores = $MAP_VCORES"
echo "mapreduce.reduce.memory.mb = $REDUCE_MEM"
echo "mapreduce.reduce.cpu.vcores = $REDUCE_VCORES"
echo "yarn.app.mapreduce.am.resource.mb = $AM_MEM"
echo "yarn.app.mapreduce.am.resource.cpu-vcores = $AM_VCORES"
echo "total containers per node: $CONTAINERS_PER_NODE"
echo "total containers for map: $MAPPERS"
row=$((row + 1))
done
特别地,有时候我们很希望能添加一行CSV就自动执行一次,而不是从头开始执行CSV提供的配置,这在测试时非常有用。此时,我们可以使用 tail -F -n +1 xxx.csv
的形式去替换 cat xxx.csv
,这将赋予脚本持续关注文件最后一行变化的能力,一但有更新,立即执行:
IFS=,
row=0
tail -F -n +1 above-csv-file.csv | while read -r CONTAINERS_PER_NODE MAP_MEM MAP_VCORES REDUCE_MEM REDUCE_VCORES AM_MEM AM_VCORES MAPPERS; do
# skip first header line
if [[ $row -eq 0 ]]; then
row=$((row+1))
continue
fi
echo "mapreduce.map.memory.mb = $MAP_MEM"
echo "mapreduce.map.cpu.vcores = $MAP_VCORES"
echo "mapreduce.reduce.memory.mb = $REDUCE_MEM"
echo "mapreduce.reduce.cpu.vcores = $REDUCE_VCORES"
echo "yarn.app.mapreduce.am.resource.mb = $AM_MEM"
echo "yarn.app.mapreduce.am.resource.cpu-vcores = $AM_VCORES"
echo "total containers per node: $CONTAINERS_PER_NODE"
echo "total containers for map: $MAPPERS"
row=$((row + 1))
done
提醒注意的是:上述脚本不会主动退出,如果是在后台运行,需要 kill 掉相关进程。