我正在开发一个 Linux 的 Bash shell 脚本,它从只留下数字的文本文件中提取数据。这些是我的示例解析数据:
3
4
4
5
6
7
8
8
9
11
Run Code Online (Sandbox Code Playgroud)
我想创建一个像这样的简单文本模式条形图,但对应于这些值:

细节:
n(parsed number) characters high 列适合我。所以在我的例子中左边的第一个条应该是 3 个字符高,第二个 4,第三个 4,第四个 5,依此类推。更准确地说,对于这个例子,一些东西(使用?字符)像:
?
?
??
???
????
?????
??????
???????
?????????
??????????
??????????
??????????
Run Code Online (Sandbox Code Playgroud)
请注意第一(左)列高 3 个字符,最后(右)列高 11 个字符。
带有$字符的相同示例,以使其更具可读性:
$
$
$$
$$$
$$$$
$$$$$
$$$$$$
$$$$$$$
$$$$$$$$$
$$$$$$$$$$
$$$$$$$$$$
$$$$$$$$$$
Run Code Online (Sandbox Code Playgroud)
我所知道的最接近的是我的进度条方法,我在另一个脚本中使用过:
printf "\033[48;5;21m" # Blue background color
for i in $(seq 1 $n); do printf " "; done # Create bar using blue spaces
Run Code Online (Sandbox Code Playgroud)
这是:填充每行打印一个带有n空格的条。但是这个条是水平的,所以在这种情况下不合适。
我请求一些核心循环示例想法来创建此条形图。
在用户 Boardrider 的建议下,接受基于任何类 Unix 工具的解决方案。也接受基于 Linux shell 的脚本语言(如 Perl 或 Python)的解决方案,只要它们用于在许多设备上实现。
这是第一次和天真的尝试......这不是一个非常有效的解决方案,因为数据被多次解析,但可能会有所帮助。在某种程度上,这是@Walter_A建议的第一个循环想法。
#!/bin/sh
#
## building a vertical bar graph of data file
## /sf/ask/2165030871/
##
## 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
Run Code Online (Sandbox Code Playgroud)
编辑 1:这是对脚本的返工。它纠正了分隔符处理(只需尝试使用“ ”或“|”作为要查看的第三个参数),但是当我使用参数 number 而不是附加变量时,它可能看起来不太可读。
编辑 2:它还处理负整数......你可以改变地面(第 5 个参数)
#!/bin/sh
#
## building a vertical bar graph of data file
## /sf/ask/2165030871/
##
## 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
Run Code Online (Sandbox Code Playgroud)
这种返工有两种口味。第一个产生与前一个类似的输出。
# 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
Run Code Online (Sandbox Code Playgroud)
注意:当所有值为正(高于地面)或负值(低于地面)时,有两个检查以避免额外的循环!好吧,也许我应该总是把“中心/图例”部分放在最后?当首先有正值和负值时,它看起来有点难看,而当只有负整数时,标签不读取相反并且带有令人不快的减号看起来很奇怪。
另请注意,这wc -L不是 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
Run Code Online (Sandbox Code Playgroud)
编辑 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
Run Code Online (Sandbox Code Playgroud)
最后的改进将是扩展能力...
| 归档时间: |
|
| 查看次数: |
5686 次 |
| 最近记录: |