【问题标题】:How do i echo specific rows and columns from csv's in a variable?如何在变量中回显 csv 中的特定行和列?
【发布时间】:2023-03-15 00:47:02
【问题描述】:

以下脚本:

#!/bin/bash
otscurrent="
AAA,33854,4528,38382,12
BBB,83917,12296,96213,13
CCC,20399,5396,25795,21
DDD,27198,4884,32082,15
EEE,2472,981,3453,28
FFF,3207,851,4058,21
GGG,30621,4595,35216,13
HHH,8450,1504,9954,15
III,4963,2157,7120,30
JJJ,51,59,110,54
KKK,87,123,210,59
LLL,573,144,717,20
MMM,617,1841,2458,75
NNN,234,76,310,25
OOO,12433,1908,14341,13
PPP,10627,1428,12055,12
QQQ,510,514,1024,50
RRR,1361,687,2048,34
SSS,1,24,25,96
TTT,0,5,5,100
UUU,294,1606,1900,85
"

IFS="," array1=(${otscurrent})

echo ${array1[4]}

打印:

$ ./test.sh 
12
BBB

我正试图让它只打印 12...而且我什至不确定如何让它只打印第 5 行第 4 列

该变量是 sqlquery 的输出,已使用多个 sed 命令解析以将格式更改为 csv。

otscurrent="$(sqlplus64 user/password@dbserverip/db as sysdba @query.sql |
 sed '1,11d; /^-/d; s/[[:space:]]\{1,\}/,/g; $d' |
 sed '$d'|sed '$d'|sed '$d' | sed '$d' |
 sed 's/Used,MB/Used MB/g'  |
 sed 's/Free,MB/Free MB/g' |
 sed 's/Total,MB/Total MB/g' |
 sed 's/Pct.,Free/Pct. Free/g' |
 sed '1b;/^Name/d' |
 sed '/^$/d'
)"

最终,我希望能够调用行和列并针对值运行语句。

最初我是把它输入:

awk -F "," 'NR>1{ if($5 < 10)  {   printf "%-30s%-10s%-10s%-10s%-10s\n", $1,$2,$3,$4,$5"%";  } else  { echo "Nothing to do" } }')"

这可行,但我无法从 if else ... 运行命令,或者至少我不知道如何。

【问题讨论】:

  • Re:选择特定的行和列,这是知识库中已经存在的其他几个问题的欺骗。
  • awk 不仅可以格式化/解析您的 csv 数据,它还可以像任何编程语言一样执行条件/循环等。

标签: arrays bash csv awk sed


【解决方案1】:

如果您有 bash 4.0 或更新版本,则关联数组是以这种形式存储数据的合适方式。

otscurrent=${otscurrent#$'\n'} # strip leading newline present in your sample data

declare -A data=( )

row=0
while IFS=, read -r -a line; do
  for idx in "${!line[@]}"; do
    data["$row,$idx"]=${line[$idx]}
  done
  (( row += 1 ))
done <<<"$otscurrent"

这使您可以访问每个单独的项目:

echo "${data[0,0]}" # first field of first line
echo "${data[9,0]}" # first field of tenth line
echo "${data[9,1]}" # second field of tenth line

【讨论】:

  • 这很有魅力......现在来了解这里的逻辑......这是一个很好的起点......我会很感激任何我能参考的东西来解释这一点。
  • 那么如果我想说任何第五个字段大于 10 的行都应该回显到屏幕上呢?
  • @substancev,为此我会使用awkawk -F, '$5 &gt; 10'
【解决方案2】:

“我试图让它只打印 12...”

问题是IFS="," 以逗号分隔,12BBB 之间没有逗号。如果您希望它们成为单独的元素,请在 IFS 中添加换行符。因此,替换:

IFS="," array1=(${otscurrent})

与:

IFS=$',\n' array1=(${otscurrent})

输出:

$ bash test.sh
12

【讨论】:

  • 最好教大家使用IFS=$',\n' read -r -d '' -a array1 &lt; &lt;(printf '%s\0' "$otscurrent");这样,他们数据中的* 就不会被文件名列表替换。
【解决方案3】:

您只需要在第 5 行打印第 4 列的值是:

$ awk -F, 'NR==5{print $4}' <<< "$otscurrent"
3453

请记住,在 awk 中,行(记录)和列(字段)编号从 1 开始,而不是 0。更多示例:

$ awk -F, 'NR==1{print $5}' <<< "$otscurrent"
12

$ awk -F, 'NR==2{print $1}' <<< "$otscurrent"
BBB

$ awk -F, '$5 > 50' <<< "$otscurrent"
JJJ,51,59,110,54
KKK,87,123,210,59
MMM,617,1841,2458,75
SSS,1,24,25,96
TTT,0,5,5,100
UUU,294,1606,1900,85

如果您想避免所有的复杂性并简单地解析您的 SQL 输出以生成您想要的结果,而无需 20 条 sed 命令,请发布一个新问题,显示原始 sqlplus 输出作为输入以及您想要的最终输出并且有人会发布一个简短、清晰、简单、高效的 awk 脚本来一次完成所有操作,或者如果您出于某种原因仍需要中间 CSV,则可能会发布 2 个命令。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-12-18
    • 2019-10-03
    • 1970-01-01
    • 1970-01-01
    • 2014-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多