数据处理与屏幕脚本编写全解析
数据处理
在数据处理领域,有多种方式对数据进行操作和解析。
分隔记录处理
对于分隔记录,我们可以使用之前定义的记录进行操作。例如:
$ split_record "$record" : user passwd uid gid name homedir shell
$ sa "$user" "$passwd" "$uid" "$gid" "$name" "$homedir" "$shell"
:root:
:x:
:0:
:0:
:root:
:/root:
:/bin/bash:
这里通过
split_record
函数将记录按冒号分隔,并将各部分赋值给相应变量,然后使用
sa
函数处理这些变量。
定长字段解析
定长字段不像分隔字段那样常见,但在某些场景下也会用到。解析定长字段时,会遍历
name=width
字符串来进行解析,许多文本编辑器从定长字段数据文件导入数据就是采用这种方式。示例代码如下:
line="John 123 Fourth Street Toronto Canada "
for nw in name=15 address=20 city=12 country=22
do
var=${nw%%=*} ## 变量名在等号之前
width=${nw#*=} ## 字段宽度在等号之后
eval "$var=\${line:0:width}" ## 提取字段
line=${line:width} ## 从记录中移除已提取的字段
done
在这个代码中,通过循环遍历
name=width
列表,依次提取定长字段并赋值给相应变量。
块文件格式处理 - PGN 国际象棋文件
PGN(Portable Game Notation)国际象棋文件是一种常见的块数据文件。它以人类和机器都可读的格式存储一个或多个国际象棋游戏。每个游戏以七个标签开始,用于标识游戏的时间、地点、参与者和结果,接着是一个空行,然后是游戏的走法。示例 PGN 文件如下:
[Event "ICS rated blitz match"]
[Site "69.36.243.188"]
[Date "2009.06.07"]
[Round "-"]
[White "torchess"]
[Black "FidelCastro"]
[Result "1-0"]
1. f4 c5 2. e3 Nc6 3. Bb5 Qc7 4. Nf3 d6 5. b3 a6 6. Bxc6+ Qxc6 7. Bb2 Nf6
8. O-O e6 9. Qe1 Be7 10. d3 O-O 11. Nbd2 b5 12. Qg3 Kh8 13. Ne4 Nxe4 14.
Qxg7#
{FidelCastro checkmated} 1-0
要解析 PGN 文件,可以使用
while
循环读取标签,然后使用
mapfile
获取游戏的走法。以下是解析 PGN 文件的代码:
pgnfile="${1:?}"
header=0
game=0
gettag() #@ 创建与标签同名且值相同的变量
{
local tagline=$1
tag=${tagline%% *} ## 获取第一个空格之前的内容
tag=${tag#?} ## 移除开头的方括号
IFS='"' read a val b <<. ## 使用双引号作为分隔符获取第二个字段
$tagline
.
eval "$tag=\$val"
}
{
while IFS= read -r line
do
case $line in
\[*) gettag "$line" ;;
"") [ -n "$Event" ] && break;; ## 跳过文件开头的空行
esac
done
mapfile -t game ## 读取文件的剩余部分
} < "$pgnfile"
## 移除数组末尾的空行
while [ -z "${game[${#game[@]}-1]}" ]
do
unset game[${#game[@]}-1]
done
## 打印带有标题的游戏信息
echo "Event: $Event"
echo "Date: $Date"
echo
set -f
printf "%4s %-10s %-10s\n" "" White Black "" ========== ========== \
"" "$White" "$Black" ${game[@]:0:${#game[@]}-1}
printf "%s\n" "${game[${#game[@]}-1]}"
解析 PGN 文件的流程如下:
graph TD;
A[开始] --> B[读取 PGN 文件路径];
B --> C[初始化变量];
C --> D[循环读取文件行];
D --> E{行是否以 [ 开头};
E -- 是 --> F[调用 gettag 函数提取标签值];
E -- 否 --> G{行是否为空};
G -- 是且 Event 变量有值 --> H[跳出循环];
G -- 否 --> D;
F --> D;
H --> I[使用 mapfile 读取文件剩余部分];
I --> J[移除数组末尾的空行];
J --> K[打印游戏信息];
K --> L[结束];
屏幕脚本编写
传统上,屏幕操作通过
termcap
或
terminfo
数据库进行,其 shell 接口是外部命令
tput
。但由于终端类型逐渐统一为 ISO 6429(也称为 ECMA - 48,以前称为 ANSI X3.64 或 VT100)标准,现在可以直接在 shell 中进行屏幕操作。
屏幕操作模式
有两种将脚本输出发送到终端屏幕的方法:
-
电传打字机模式
:将终端视为打印机或电传打字机,每打印一行,屏幕图像向上滚动,旧行消失。这种模式简单,适用于许多应用场景。
-
画布模式
:将屏幕视为黑板或画布,可以在屏幕的特定位置打印内容,擦除和覆盖之前的内容,还可以按列或在特定位置打印文本,使终端成为随机访问设备。
画布操作基础
要将屏幕用作画布,最重要的是能够将光标定位到屏幕上的任何位置。定位光标的序列是
ESC[<ROW>;<COL>H
,转换为
printf
格式字符串后可以直接使用或封装在函数中。示例代码如下:
cu_row_col=$'\e[%d;%dH'
printf "$cu_row_col" 5 10 ## 第 5 行,第 10 列
echo "Here I am!"
以下是一些屏幕操作的函数和变量定义:
# screen-vars 文件
ESC=$'\e'
CSI=$ESC[
# 移动光标到屏幕左上角
topleft=${CSI}H
# 清屏
cls=${CSI}J
# 清屏并移动到屏幕左上角
clear=$topleft$cls
# 清除从光标到行尾的内容
clearEOL=${CSI}K
# 清除从光标到行首的内容
clearBOL=${CSI}1K
# 清除从光标到屏幕末尾的内容
clearEOS=${CSI}0J
# 清除从光标到屏幕开头的内容
clearBOS=${CSI}1J
# 光标移动字符串
cu_up=${CSI}%sA
cu_down=${CSI}%sB
cu_right=${CSI}%sC
cu_left=${CSI}%sD
# 关闭和打开光标
cu_hide=${CSI}?25l
cu_show=${CSI}?12l${CSI}?25h
# 保存光标位置
cu_save=${CSI}s
# 恢复光标到保存的位置
cu_restore=${CSI}u
# 移动光标到块中的下一行/上一行
cu_NL=$cu_restore${cu_down/\%s/}$cu_save
cu_PL=$cu_restore${cu_up/\%s/}$cu_save
这些变量和函数提供了基本的屏幕操作能力,如清屏、移动光标、保存和恢复光标位置等。
屏幕操作示例 - screen-demo1
. screen-funcs ## 引入屏幕操作函数库
printf "$clear$cu_hide" ## 清屏并隐藏光标
printat 10 10 "${cu_save}XX" ## 移动光标、保存位置并打印 XX
sleep 1 ## 暂停 1 秒
printat 20 20 "20/20" ## 移动光标并打印
sleep 1 ## 暂停 1 秒
printf "$cu_restore$cu_down${cu_save}YY" ## 恢复位置、移动光标、打印并保存位置
sleep 1 ## 暂停 1 秒
printf "$cu_restore$cu_down${cu_save}ZZ" 4 ## 恢复位置、移动光标、打印并保存位置
sleep 1 ## 暂停 1 秒
printat 1 1 "$cu_show" ## 移动到屏幕左上角并显示光标
在这个示例中,通过调用
printat
函数和使用光标移动、保存和恢复的变量,实现了在屏幕上不同位置打印内容并控制光标的操作。
颜色和属性设置
字符可以以粗体、下划线、反显等模式以及各种颜色打印(前提是终端支持)。这些属性通过
ESC[ATTRm
序列进行修改,其中
ATTR
是属性或颜色的编号。以下是颜色和属性的变量定义:
# 颜色定义
black=0
red=1
green=2
yellow=3
blue=4
magenta=5
cyan=6
white=7
# 前景色和背景色前缀
fg=3
bg=4
# 属性定义
bold=1
underline=4
reverse=7
# 设置颜色
set_bg="${CSI}4%dm" ## 设置背景颜色
set_fg="${CSI}3%dm" ## 设置前景颜色
set_fgbg="${CSI}3%d;4%dm" ## 设置前景和背景颜色
以下是一个颜色和属性设置的示例脚本:
. screen-funcs
echo
for attr in "$underline" 0 "$reverse" "$bold" "$bold;$reverse"
do
printf "$set_attr" "$attr"
printf "$set_fg %s " "$red" RED
printf "$set_fg %s " "$green" GREEN
printf "$set_fg %s " "$blue" BLUE
printf "$set_fg %s " "$black" BLACK
printf "\e[m\n"
done
echo
在这个脚本中,通过循环设置不同的属性和颜色,打印出不同颜色和样式的文本。
文本块放置
put_block
函数可以在当前光标位置逐行打印参数,
put_block_at
函数可以将光标移动到指定位置,然后调用
put_block
函数打印参数。示例代码如下:
put_block() #@ 在当前位置开始以块的形式打印参数
{
printf "$cu_save" ## 保存光标位置
printf "%s$cu_NL" "$@" ## 恢复光标位置、向下移动一行并保存光标
}
put_block_at() #@ 在 $1 和 $2 指定的位置以块的形式打印参数
{
printat "$1" "$2"
shift 2
put_block "$@"
}
以下是使用
put_block_at
函数的示例脚本:
. screenfuncs
printf "$cls"
put_block_at 3 12 First Second Third Fourth Fifth
put_block_at 2 50 January February March April May June July
这个脚本在屏幕上以列的形式显示数据块。
文本换行
当要打印的文本是一个字符串时,可以使用
wrap
函数将其拆分为不超过指定最大宽度的行。示例代码如下:
wrap() #@ 用法:wrap 字符串 长度
{ #@ 需要 bash - 3.1 或更高版本
local words=$1 textwidth=$2 line= opts=$-
local len=0 templen=0
set -f
unset -v wrap
for word in $words
do
templen=$(( $len + 1 + ${#word} )) ## 测试添加一个单词后的长度
if [ "$templen" -gt "$textwidth" ] ## 添加单词是否超过长度限制?
then
wrap+=( "$line" ) ## 是,将当前行存储到数组中
printf -v line "%s" "$word" ## 开始新的一行
len=${#word}
else
len=$templen ## 否,将单词添加到当前行
printf -v line "%s" "${line:+"$line "}" "$word"
fi
done
wrap+=( "$line" )
case $opts in
*f*) ;;
*) set +f ;;
esac
}
以下是使用
wrap
函数的示例脚本:
clear
wrap "The quick brown fox jumps over the lazy dog" 15
x=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
printat 1 1
printf "%s\n" $x{,,,,,,,,,,} ## 打印 11 行 'x'
print_block_at 3 33 "${wrap[@]}"
printat 12 1
这个脚本将字符串拆分为不超过 15 个字符的行,并在屏幕上打印出来。
文本滚动
通过结合数组和子字符串扩展,可以在屏幕的任何区域滚动文本。示例代码如下:
list=( /usr/bin/* ) ## 可以尝试其他目录或列表
rows=9 ## 滚动区域的行数
delay=.01 ## 滚动间隔时间
width=-33.33 ## 宽度规格:(不超过)33 个字符,左对齐
x=XXXXXXXXXXXXXXXXXXXXXXXXXX ## 'X' 组成的条
x=$x$x$x$x ## 更长的条
clear ## 清屏
printf "%50.50s\n" $x{,,,,,,,,,,,,,} ## 打印 14 行 'X'
n=0 ## 从第一个元素开始显示
## 向上滚动直到到达底部
while [ $(( n += 1 )) -lt $(( ${#list[@]} - $rows )) ]
do
printf "\e[3;1H"
printf "\e[7C %${width}s\n" "${list[@]:n:rows}"
# read -sn1 -t "$delay" && break
done
sleep 1
## 向下滚动直到到达顶部
while [ $(( n -= 1 )) -ge 0 ]
do
printf "\e[3;1H"
printf "\e[7C %${width}s\n" "${list[@]:n:rows}"
# read -sn1 -t "$delay" && break
done
printf "\e[15;1H" ## 结束时将光标移到滚动区域下方
在这个脚本中,将
/usr/bin/
目录下的文件名存储在数组中,然后在屏幕上滚动显示这些文件名。
骰子绘制
使用 shell 可以轻松实现骰子的图形化显示。以下是一个绘制骰子的示例代码:
pip=o ## 骰子点数的字符
p0=" " ## 空白行
p1=" $pip " ## 左侧一个点数
p2=" $pip " ## 中间一个点数
p3=" $pip " ## 右侧一个点数
p4=" $pip $pip " ## 两个点数
p5=" $pip $pip $pip " ## 三个点数
cs=$'\e7' ## 保存光标位置
cr=$'\e8' ## 恢复光标位置
dn=$'\e[B' ## 向下移动一行
b=$'\e[1m' ## 设置粗体属性
cu_put='\e[%d;%dH' ## 定位光标的格式字符串
fgbg='\e[3%d;4%dm' ## 设置颜色的格式字符串
dice=(
## 点数为 1 到 6 的骰子(数组元素 0 到 5)
"$b$cs$p0$cr$dn$cs$p0$cr$dn$cs$p2$cr$dn$cs$p0$cr$dn$p0"
"$b$cs$p0$cr$dn$cs$p1$cr$dn$cs$p0$cr$dn$cs$p3$cr$dn$p0"
"$b$cs$p0$cr$dn$cs$p1$cr$dn$cs$p2$cr$dn$cs$p3$cr$dn$p0"
"$b$cs$p0$cr$dn$cs$p4$cr$dn$cs$p0$cr$dn$cs$p4$cr$dn$p0"
"$b$cs$p0$cr$dn$cs$p4$cr$dn$cs$p2$cr$dn$cs$p4$cr$dn$p0"
"$b$cs$p0$cr$dn$cs$p5$cr$dn$cs$p0$cr$dn$cs$p5$cr$dn$p0"
)
clear
printf "$cu_put" 2 5 ## 定位光标
printf "$fgbg" 7 0 ## 白色前景,黑色背景
printf "%s\n" "${dice[RANDOM%6]}" ## 打印随机骰子
printf "$cu_put" 2 20 ## 定位光标
printf "$fgbg" 0 3 ## 黑色前景,黄色背景
printf "%s\n" "${dice[RANDOM%6]}" ## 打印随机骰子
在这个脚本中,定义了一个包含六个骰子图形的数组,通过随机选择数组元素并设置颜色和位置,在屏幕上打印出两个随机骰子。
通过以上介绍,我们了解了数据处理和屏幕脚本编写的多种方法和技巧,这些技术可以帮助我们更好地处理数据和实现交互式的屏幕显示。
数据处理与屏幕脚本编写全解析
数据处理与屏幕脚本编写的综合应用与拓展
数据处理的拓展应用
在实际应用中,数据处理的需求往往更加复杂。例如,对于数组操作,我们可以对之前提到的排序和搜索函数进行优化。当数组规模超过一定大小时,可以使用
sort
和
grep
等工具来提高效率。以下是一个简单的思路:
isort() {
local arr=("$@")
if [ ${#arr[@]} -gt 100 ]; then
printf "%s\n" "${arr[@]}" | sort
else
# 原排序算法
for ((i = 0; i < ${#arr[@]}; i++)); do
for ((j = i + 1; j < ${#arr[@]}; j++)); do
if [[ "${arr[$i]}" > "${arr[$j]}" ]]; then
tmp="${arr[$i]}"
arr[$i]="${arr[$j]}"
arr[$j]="$tmp"
fi
done
done
printf "%s\n" "${arr[@]}"
fi
}
asearch() {
local arr=("$@")
local search_term="${arr[-1]}"
unset arr[-1]
if [ ${#arr[@]} -gt 100 ]; then
printf "%s\n" "${arr[@]}" | grep "$search_term"
else
# 原搜索算法
for element in "${arr[@]}"; do
if [[ "$element" == *"$search_term"* ]]; then
echo "$element"
fi
done
fi
}
另外,对于网格数据的处理,我们可以编写一个函数来转置网格的行和列。以下是实现代码:
transpose_grid() {
local grid=("$@")
local rows=${#grid[@]}
local cols=$((${#grid[0]} / 1)) # 假设每个元素长度为 1
local transposed=()
for ((j = 0; j < cols; j++)); do
local new_row=""
for ((i = 0; i < rows; i++)); do
new_row="$new_row${grid[$i]:$j:1}"
done
transposed+=("$new_row")
done
printf "%s\n" "${transposed[@]}"
}
# 示例使用
grid=("123" "456" "789")
transpose_grid "${grid[@]}"
还可以将网格处理函数扩展到非正方形网格,例如 6×3 的网格。以下是一个简单的示例:
process_non_square_grid() {
local grid=("$@")
local rows=${#grid[@]}
local cols=$((${#grid[0]} / 1))
# 处理非正方形网格的逻辑
for ((i = 0; i < rows; i++)); do
for ((j = 0; j < cols; j++)); do
printf "%s " "${grid[$i]:$j:1}"
done
printf "\n"
done
}
# 示例使用
non_square_grid=("123" "456" "789" "abc" "def" "ghi")
process_non_square_grid "${non_square_grid[@]}"
屏幕脚本编写的拓展应用
在屏幕脚本编写方面,我们可以进一步优化和拓展之前的功能。例如,对于
put_block
和
put_block_at
函数,当屏幕上已经有很多文本时,输出可能会被遮挡。我们可以使用
print_block
和
print_block_at
函数来清除文本块周围的矩形区域。以下是相关代码:
_max_length() {
local var
_MAX_LENGTH=${#1}
shift
for var; do
[ "${#var}" -gt "$_MAX_LENGTH" ] && _MAX_LENGTH=${#var}
done
}
print_block() {
local _MAX_LENGTH
_max_length "$@"
printf "$cu_save"
printf " %-${_MAX_LENGTH}s $cu_NL" " " "$@" " "
}
print_block_at() {
printat $1 $2
shift 2
print_block "$@"
}
另外,对于文本换行和滚动功能,我们可以结合起来实现更复杂的效果。例如,在滚动区域内显示换行后的文本。以下是一个示例脚本:
list=("This is a long text that needs to be wrapped and scrolled." "Another line of text for demonstration.")
rows=9
delay=.01
width=33
x=XXXXXXXXXXXXXXXXXXXXXXXXXX
x=$x$x$x$x
clear
printf "%50.50s\n" $x{,,,,,,,,,,,,,}
n=0
while [ $(( n += 1 )) -lt $(( ${#list[@]} - $rows )) ]; do
local wrapped_lines=()
for line in "${list[@]:n:rows}"; do
wrap "$line" "$width"
wrapped_lines+=("${wrap[@]}")
done
printf "\e[3;1H"
for wrapped_line in "${wrapped_lines[@]}"; do
printf "\e[7C %s\n" "$wrapped_line"
done
# read -sn1 -t "$delay" && break
sleep "$delay"
done
sleep 1
while [ $(( n -= 1 )) -ge 0 ]; do
local wrapped_lines=()
for line in "${list[@]:n:rows}"; do
wrap "$line" "$width"
wrapped_lines+=("${wrap[@]}")
done
printf "\e[3;1H"
for wrapped_line in "${wrapped_lines[@]}"; do
printf "\e[7C %s\n" "$wrapped_line"
done
# read -sn1 -t "$delay" && break
sleep "$delay"
done
printf "\e[15;1H"
总结
本文详细介绍了数据处理和屏幕脚本编写的相关知识和技术。在数据处理方面,涵盖了分隔记录处理、定长字段解析、块文件格式处理等内容,还给出了一些拓展应用的示例,如数组排序和搜索的优化、网格数据处理等。在屏幕脚本编写方面,介绍了屏幕操作模式、画布操作基础、颜色和属性设置、文本块放置、文本换行、文本滚动和骰子绘制等功能,并且提供了相应的代码示例和拓展应用。
通过掌握这些技术,我们可以更好地处理各种数据,实现交互式的屏幕显示,为开发更加复杂和功能强大的脚本程序奠定基础。以下是一个总结表格:
| 领域 | 主要内容 | 示例代码 |
|---|---|---|
| 数据处理 | 分隔记录处理、定长字段解析、PGN 文件处理、数组操作优化、网格处理 |
多个代码块展示,如
split_record
示例、
wrap
函数等
|
| 屏幕脚本编写 | 屏幕操作模式、画布操作、颜色属性设置、文本处理、滚动和绘制 |
多个代码块展示,如
printat
函数、
dice
脚本等
|
未来,我们可以根据实际需求,进一步拓展和优化这些技术,例如结合更多的外部工具和库,实现更高效、更复杂的数据处理和屏幕交互效果。
graph LR;
A[数据处理] --> B[分隔记录处理];
A --> C[定长字段解析];
A --> D[块文件处理];
A --> E[数组操作优化];
A --> F[网格处理];
G[屏幕脚本编写] --> H[屏幕操作模式];
G --> I[画布操作];
G --> J[颜色属性设置];
G --> K[文本处理];
G --> L[滚动和绘制];
通过以上的总结和拓展,我们对数据处理和屏幕脚本编写有了更全面和深入的理解,希望这些内容能对大家在实际应用中有所帮助。
超级会员免费看
1984

被折叠的 条评论
为什么被折叠?



