gpt4 book ai didi

bash - 外壳 : How can I make a text-mode bar chart from parsed data (numbers)?

转载 作者:行者123 更新时间:2023-11-29 09:21:28 25 4
gpt4 key购买 nike

我正在开发一个 Linux 的 Bash shell 脚本,它从一个文本文件中提取数据,只留下数字。这些是我的示例解析数据:

3
4
4
5
6
7
8
8
9
11

我想创建一个像这样的简单文本模式条形图,但对应于这些值:

Bar chart

详细信息:

  • 我需要图表是垂直的。
  • 第一个数字应显示在左侧,最新数字应显示在右侧
  • n(已解析数字)个字符高列适合我。所以在我的例子中左边的第一个栏应该是 3 个字符高,第二个是 4 个,第三个是 4 个,第四个是 5 个,依此类推。

更准确地说,对于这个例子,一些东西(使用 字符)像:



██
███
████
█████
██████
███████
█████████
██████████
██████████
██████████

请注意第一列(左)列高 3 个字符,最后一列(右)列高 11 个字符。
使用 $ 字符的相同示例,以使其更具可读性:

         $
$
$$
$$$
$$$$
$$$$$
$$$$$$
$$$$$$$
$$$$$$$$$
$$$$$$$$$$
$$$$$$$$$$
$$$$$$$$$$

到目前为止,我所知道的最接近的方法是我在其他脚本中使用的进度条方法:

printf "\033[48;5;21m"   # Blue background color
for i in $(seq 1 $n); do printf " "; done # Create bar using blue spaces

这是:用 n 空格填充每行打印一个条。但是这个条是水平的,所以在这种情况下不合适。

我请求一些核心循环示例想法来创建此条形图。

在用户 Boardrider 的建议下,接受基于任何类 unix 工具的解决方案。基于用于 Linux shell 的脚本语言(如 Perl 或 Python)的解决方案也被接受,只要它们曾经在许多设备上实现。

最佳答案

这是第一次天真的尝试...这不是一个非常有效的解决方案,因为数据会被多次解析,但它可能会有所帮助。在某种程度上,这是 @Walter_A 提出的第一个循环想法。 .

#!/bin/sh
#
## building a vertical bar graph of data file
## https://stackoverflow.com/q/30929012
##
## 1. required. Data file with one value per line and nothing else!
## /!\ provide the (relative or absolute) file path, not file content
: ${1:?" Please provide a file name"}
test -e "$1" || { echo "Sorry, can't find $1" 1>&2 ; exit 2 ; }
test -r "$1" || { echo "Sorry, can't access $1" 1>&2 ; exit 2 ; }
test -f "$1" || { echo "Sorry, bad format file $1" 1>&2 ; exit 2 ; }
test $( grep -cv '^[0-9][0-9]*$' "$1" 2>/dev/null ) -ne 0 || { echo "Sorry, bad data in $1" 1>&2 ; exit 3 ; }
# setting characters
## 2. optional. Ploting character (default is Dollar sign)
## /!\ for blank color use "\033[48;5;21m \033[0m" or you'll mess...
c_dot="$2"
: ${c_dot:='$'}
## 3. optional. Separator characher (default is Dash sign)
## /!\ as Space is not tested there will be extra characters...
c_sep="$3"
: ${c_sep:='-'}
# init...
len_w=$(wc -l "$1" | cut -d ' ' -f 1 )
l_sep=''
while test "$len_w" -gt 0
do
l_sep="${l_sep}${c_sep}";
len_w=$(($len_w-1))
done
unset len_w
# part1: chart
echo ".${c_sep}${l_sep}${c_sep}."
len_h=$(sort -n "$1" | tail -n 1)
nbr_d=${#len_h}
while test "$len_h" -gt 0
do
printf '| '
for a_val in $(cat "$1")
do
test "$a_val" -ge "$len_h" && printf "$c_dot" || printf ' '
done
echo ' |'
len_h=$(($len_h-1))
done
unset len_h
# part2: legend
echo "|${c_sep}${l_sep}${c_sep}|"
while test "$nbr_d" -gt 0
do
printf '| '
for a_val in $(cat "$1")
do
printf "%1s" $(echo "$a_val" | cut -c "$nbr_d")
done
echo ' |'
nbr_d=$(($nbr_d-1))
done
unset nbr_d
# end
echo "'${c_sep}${l_sep}${c_sep}'"
unset c_sep
exit 0

编辑 1:这是对脚本的修改。它纠正了分隔符处理(只需尝试使用 ' ' 或 '|' 作为要查看的第三个参数),但它可能看起来不太可读,因为我使用参数编号而不是附加变量。

编辑 2:它还处理负整数...您可以更改地面(第 5 个参数)

#!/bin/sh
#
## building a vertical bar graph of data file
## https://stackoverflow.com/q/30929012
##
## 1. required. Data file with one value per line and nothing else!
## /!\ provide the (relative or absolute) file path, not file content
: ${1:?" Please provide a file name"}
[ -e "$1" ] || { echo "Sorry, can't find $1" 1>&2 ; exit 2 ; }
[ -r "$1" ] || { echo "Sorry, can't access $1" 1>&2 ; exit 2 ; }
[ -f "$1" ] || { echo "Sorry, bad format file $1" 1>&2 ; exit 2 ; }
[ $( grep -cv '^[-0-9][0-9]*$' "$1" 2>/dev/null ) -ne 0 ] || { echo "Sorry, bad data in $1" 1>&2 ; exit 3 ; }
## /!\ following parameters should result to a single character
## /!\ for blank color use "\033[48;5;21m \033[0m" or you'll mess...
## 2. optional. Ploting character (default is Dollar sign)
## 3. optional. Horizontal border characher (default is Dash sign)
## 4. optional. Columns separator characher (default is Pipe sign)
## (!) however, when no arg provided the graph is just framed in a table
## 5. optional. Ground level integer value (default is Zero)
test "${5:-0}" -eq "${5:-0}" 2>/dev/null || { echo "oops, bad parameter $5" 1>&2 ; exit 3 ; }
# init...
_long=$(wc -l < "$1" ) # width : number of data/lines in file
if [ -n "$4" ]
then
_long=$((_long*2-3))
fi
_line=''
while [ "$_long" -gt 0 ]
do
_line="${_line}${3:--}"
_long=$((_long-1))
done
unset _long
_from=$(sort -n "$1" | tail -n 1 ) # max int
_stop=$(sort -n "$1" | head -n 1 ) # min int

这次返工有两种形式。第一个产生与前一个类似的输出。

# begin
echo "${4-.}${3:--}${_line}${3:--}${4-.}"
# upper/positive
if [ $_from -gt ${5:-0} ]
then
while [ $_from -gt ${5:-0} ]
do
printf "${4:-| }"
for _cint in $(cat "$1" )
do
if [ $_cint -ge $_from ]
then
printf "${2:-$}$4"
else
printf " $4"
fi
done
echo " ${4:-|}"
_from=$((_from-1))
done
echo "${4-|}${3:--}${_line}${3:--}${4-|}"
fi
unset _from
# center/legend
_long=$(wc -L < "$1" ) # height : number of chararcters in longuest line...
while [ $_long -ge 0 ]
do
printf "${4:-| }"
for _cint in $(cat "$1" )
do
printf "%1s$4" $(echo "$_cint" | cut -c "$_long" )
done
echo " ${4:-|}"
_long=$((_long-1))
done
unset _long
# lower/negative
if [ $_stop -lt ${5:-0} ]
then
_from=${5:-0}
echo "${4-|}${3:--}${_line}${3:--}${4-|}"
while [ $_from -gt $_stop ]
do
printf "${4:-| }"
for _cint in $(cat "$1" )
do
if [ $_cint -lt $_from ]
then
printf "${2:-$}$4"
else
printf " $4"
fi
done
echo " ${4:-|}"
_from=$((_from-1))
done
fi
unset _stop
# end
echo "${4-'}${3:--}${_line}${3:--}${4-'}"
exit 0

注意:当所有值为正(高于地面)或负(低于地面)时,有两次检查以避免额外循环!好吧,也许我应该总是把“中心/图例”部分放在最后?当同时存在正值和负值时,它看起来有点难看,而当只有负整数时,它看起来很奇怪,标签不读相反并且有令人不快的减号。
还要注意 wc -L is not POSIX... ...因此可能需要另一个循环。

这是另一个变体,其图例编号大小合适,而不是底部。这样做,我节省了一个额外的循环,但我不太喜欢输出(我更喜欢左边的值而不是右边的值,但这是一种味道,不是吗?)

# begin
printf "${4-.}${3:--}${_line}${3:--}${4-.}"
# upper/positive
if [ $_from -gt ${5:-0} ]
then
echo ""
while [ $_from -gt ${5:-0} ]
do
_ctxt=''
printf "${4:-| }"
for _cint in $(cat "$1" )
do
if [ $_cint -ge $_from ]
then
printf "${2:-$}$4"
else
printf " $4"
fi
if [ $_cint -eq $_from ]
then
_ctxt="_ $_from"
fi
done
echo " ${4:-}${_ctxt}"
_from=$((_from-1))
done
_from=$((_from+1))
else
echo "_ ${1}"
fi
# center/ground
if [ $_stop -lt ${5:-0} ] && [ $_from -gt ${5:-0} ]
then
echo "${4-|}${3:--}${_line}${3:--}${4-|}_ ${1}"
fi
# lower/negative
if [ $_stop -lt ${5:-0} ]
then
_from=${5:-0}
while [ $_from -gt $_stop ]
do
_ctxt=''
printf "${4:-| }"
for _cint in $(cat "$1" )
do
if [ $_cint -lt $_from ]
then
printf "${2:-$}$4"
else
printf " $4"
fi
if [ $_cint -eq $((_from-1)) ]
then
_ctxt="_ $_cint"
fi
done
echo " ${4:-|}${_ctxt}"
_from=$((_from-1))
done
fi
# end
unset _from
printf "${4-'}${3:--}${_line}${3:--}${4-'}"
if [ $_stop -lt ${5:-0} ]
then
echo ""
else
echo "_ ${1}"
fi
unset _stop
exit 0

编辑 3:有一些额外的检查,因此当只有正数或负数时不会添加额外的底线。

最后,我认为最终的解决方案是两者的混合,其中值显示在侧面,值的位置显示在中心。然后它更接近 GNU Plot 的输出。

# init...
_long=$(wc -l < "$1" )
if [ -n "$4" ]
then
_long=$((_long*2-3))
fi
_line=''
while [ $_long -gt 0 ]
do
_line="${_line}${3:--}"
_long=$((_long-1))
done
unset _long
_from=$(sort -n "$1" | tail -n 1 ) # max int
_stop=$(sort -n "$1" | head -n 1 ) # min int
# begin
echo "${4-.}${3:--}${_line}${3:--}${4-.}"
# upper/positive
if [ $_from -gt ${5:-0} ]
then
while [ $_from -gt ${5:-0} ]
do
_ctxt=''
printf "${4:-| }"
for _cint in $(cat "$1" )
do
if [ $_cint -ge $_from ]
then
printf "${2:-$}$4"
else
printf " $4"
fi
if [ $_cint -eq $_from ]
then
_ctxt="_ $_from"
fi
done
echo " ${4:-|}$_ctxt"
_from=$((_from-1))
done
echo "${4-|}${3:--}${_line}${3:--}${4-|}"
fi
# center/ground
_size=$(wc -l < "$1" ) # width : number of data/lines in file
##_long=${#_size} # height : number of chararcters in long
#_long=1
##while [ $_long -gt 0 ]
#while [ $_long -le ${#_size} ]
#do
#_rank=1
#printf "${4:-| }"
#while [ $_rank -le $_size ]
#do
#printf "%1s$4" $( printf "%0${#_size}d" $_rank | cut -c $_long )
#_rank=$((_rank+1))
#done
#printf " ${4:-|}"
##_long=$((_long-1))
#_long=$((_long+1))
##if [ $_long -eq 0 ]
#if [ $_long -eq ${#_size} ]
#then
#printf "_ ${1}"
#fi
#echo ''
#done
_rank=1
printf "${4:-| }"
while [ $_rank -le $_size ]
do
printf "%1s$4" $( expr "$_rank" : '.*\(.\)$' )
_rank=$((_rank+1))
done
echo " ${4:-|}_ ${1}"
# lower/negative
if [ $_stop -lt ${5:-0} ]
then
echo "${4-|}${3:--}${_line}${3:--}${4-|}"
while [ $_from -gt $_stop ]
do
_ctxt=''
printf "${4:-| }"
for _cint in $(cat "$1" )
do
if [ $_cint -lt $_from ]
then
printf "${2:-$}${4}"
else
printf " $4"
fi
if [ $_cint -eq $((_from-1)) ]
then
_ctxt="_ $_cint"
fi
done
echo " ${4:-|}$_ctxt"
_from=$((_from-1))
done
fi
unset _from
unset _stop
# end
echo "${4-'}${3:--}${_line}${3:--}${4-'}"
exit 0

最后的改进是扩展能力......

关于bash - 外壳 : How can I make a text-mode bar chart from parsed data (numbers)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30929012/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com