我遇到了以意外顺序回显输出的 bash 脚本问题。脚本如下。问题在于第 30-32 行的输出。
1 IFS=$'\n'
2 i=1
3 bluered=""
4 blueyellow=""
5 redyellow=""
6 all=""
7 while [ $i -le `cat sorted.csv | wc -l` ]
8 do
9 for j in {0..2}
10 do
11 # cat sorted.csv | head -$i | tail -1 | awk -F',' '{print $1}'
12 declare "`cat sorted.csv | head -$i | tail -1 | awk -F',' '{print $1}'`=`cat sorted.csv | head -$i | tail -1 | awk -F',' '{print $5}'`"
13 i=$((i+1))
14 done
15
16 if [[ ${blue} == ${red} ]]; then bluered=1; else bluered=0; fi
17 if [[ ${blue} == ${yellow} ]]; then blueyellow=1; else blueyellow=0; fi
18 if [[ ${red} == ${yellow} ]]; then redyellow=1; else redyellow=0; fi
19 if [[ ${blue} == ${red} ]] && [[ ${red} == ${yellow} ]]; then all=1; else all=0; fi
20
21 echo "`cat sorted.csv | head -$((i-3)) | tail -1`"
22 echo ",$all,$bluered,$blueyellow,$redyellow"
23 echo "`cat sorted.csv | head -$((i-2)) | tail -1`"
24 echo ",$all,$bluered,$blueyellow,$redyellow"
25 echo "`cat sorted.csv | head -$((i-1)) | tail -1`"
26 echo ",""$all"",""$bluered"",""$blueyellow"",""$redyellow"
27
28
29
30 echo "`cat sorted.csv | head -$((i-3)) | tail -1`,$all,$bluered,$blueyellow,$redyellow"
31 echo "`cat sorted.csv | head -$((i-2)) | tail -1`"",$all,$bluered,$blueyellow,$redyellow"
32 echo "`cat sorted.csv | head -$((i-1)) | tail -1`"",""$all"",""$bluered"",""$blueyellow"",""$redyellow"
33 done
第 30-32 行的双引号格式略有不同,因为我尝试了不同的方法来使其正常工作。第 21-26 行无非是将第 30-32 行分解为两部分(即第 21-22 行与第 30 行相同)。
基于输入文件“sorted.csv”,第 30-32 行(对于输入文件的前 3 行)的正确输出应该是:
blue,1,WCC131035882,0,e89d89d7ca7c502ca8d3b2e0d7c4980dba346a63d57a437d8f1428065fb83e9f,0,0,0,1
red,1,Z292V5DB,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68,0,0,0,2
yellow,1,Z292V94K,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68,0,0,0,1
但实际输出是:
,0,0,0,1CC131035882,0,e89d89d7ca7c502ca8d3b2e0d7c4980dba346a63d57a437d8f1428065fb83e9f #(line 30 output)
,0,0,0,192V5DB,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68 #(line 31 output)
,0,0,0,1,Z292V94K,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68 #(line 32 output)
第 21 - 26 行返回以下输出:
blue,1,WCC131035882,0,e89d89d7ca7c502ca8d3b2e0d7c4980dba346a63d57a437d8f1428065fb83e9f #(line 21 output)
,0,0,0,1 #(line 22 output)
red,1,Z292V5DB,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68 #(line 23output)
,0,0,0,1 #(line 24 output)
yellow,1,Z292V94K,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68 #(line 25)
,0,0,0,1 #(line 26 output)
简而言之,我想使用 3 个单行命令(如第 30-32 行(但语法正确))连接第 21-22、23-24 和 25-26 行的输出。注意,第 21-26 行仅包含在该脚本用于演示第 30 行(31 或 32)的两部分在分成两行时是否正常工作。目前,第 30 行有效地连接了第 22 行和第 21 行的输出,而不是第 21 行和第 22 行的输出。但是,在进行这种反向连接时,它还截断了第 21 行输出的前 8 个字符(注意,输出第 22 行正好是 8 个字符)。
我如何正确编写第 30-32 行以便它们创建所需的输出?
提前感谢你的帮助。
比简洁的“使用
dos2unix
”更具洞察力。显然
sorted.csv
使用 CR+LF 行尾,而它应该只使用 LF。当您
`something`
在输出的末尾使用换行符 (LF) 时,something
会去除,但不会去除回车符 (CR)。在你的情况下text+CR+LF
成为text+CR
. 如果它是 的唯一输入echo
,该工具会像往常一样添加一个换行符,并且最后会有 CR+LF。在打印到控制台时,此 CR 没有任何变化。但是如果
echo "`foo`bar"
CR 返回的字符foo
停留在结果字符串的中间,那么后面的任何内容都会从控制台的左边缘打印出来,覆盖前面的部分。dos2unix sorted.csv
正如您已经指出的那样,解决方案是使用。但还有更多:
cat
$(stuff)
和 和有什么不一样`stuff`
?printf
比echo
?""
(例如在第 32 行中)什么都不做。他们是关闭+打开,而不是打开+关闭。他们只会混淆代码。我知道它们是“实验性的”,我明确地说它们没有任何改变。也许_
echo $(stuff)
or有什么问题echo `stuff`
?我承认这个反对意见在这里是有问题的。
"`foo`bar"
从输出中去除任何尾随的 LFfoo
并将其与连接起来很有用bar
;在你的情况下这很重要。然后使用 like 语法echo "`foo`"
来演示行为的相关差异,您明确说明了这一点。dos2unix
当然没有回答这个问题,这是[强调我的]:正确编写的第 32 行至少会使用
printf
, nocat
。$( … )
反引号和过多的引号可能会保留,尽管代码在合理引用的情况下更具可读性。您也可以考虑将格式与数据分开,这很容易printf
:另外其他地方也有不好的解决办法:
while [ $i -le `cat sorted.csv | wc -l` ]
(除了cat
和反引号)。无需获取每次迭代的行数。wc -l sorted.csv
应该在循环之前运行一次,它的结果存储在一个变量中(除非你期望行数在执行过程中发生变化,但我认为你不会;如果数字不变,脚本的逻辑更有意义).你一次又一次地重新阅读文件。行数越多,您重新打开、从头开始阅读并每次选择一行的次数就越多。应该重新设计流程,以便逐行解析文件,可能没有 prior
wc -l
,可能以类似管道的方式(即只打开一次,只读取一次而不跳回)。由于您使用declare
内置的,while IFS= read -r … ; done <sorted.csv
可能是必须的(也许是 preliminaryawk
,例如<sorted.csv awk … | while IFS= read -r …
)。你可以read
对三个不同的变量进行三次操作,然后执行操作;然后阅读接下来的三行。read
本身效率低下,但比多次重新打开文件更优雅。请注意,文件中每增加一行,脚本的效率就会降低;如果文件大到足以read
表现出低效率,您的方法可能会执行得更糟。如果你愿意,这整个改变将不会是微不足道的。
文件名不应该在这么多地方硬编码。只读取一次文件自然会减少这个问题。即便如此,开始使用
input_file="sorted.csv"
并"$input_file"
在任何需要的地方使用也是好的。如果您决定将文件路径作为命令行参数传递,那么只需键入input_file="$1"
.为什么没有shebang?
考虑到大局,更正确的第 30-32 行应该是这个片段:
可能仅
awk
作为过滤器工作就可以做到这一点;该文件将通过管道传输给它。您也可以将信息存储在awk
变量中,以便以后使用。条件也可用。像这个未经充分测试的例子:(注意:
awk
我用 is in fact 测试过mawk
)。保存它,使其可执行,通过管道将sorted.csv
其传递给它(例如<sorted.csv ./the_script
)。关于回车在文件中的评论是正确的。在运行输入文件 thought dox2unix 后,脚本按预期运行。谢谢,戈登。