【问题标题】:How do I print 2 columns with looping from a file in bash如何从 bash 中的文件循环打印 2 列
【发布时间】:2021-01-28 03:56:25
【问题描述】:

我有以下output.txt 它只包含 2 列来演示:

Test1 Test1-IS-OK
Test2 Test2-IS-NOT
Test3 Test3-IS-OK
Test4 Test4-IS-OK
Test5 Test5-IS-NOT

那么我的 bash 脚本有以下代码:

#!/bin/bash
output="output.txt"
a=$(awk '{ print $1 }' $output)
b=$(awk '{ print $2 }' $output)

while IFS=" " read -r $a $b
do
    echo "LOG: $a and $b"
done < "$output"

我收到以下错误:

./test.sh: line 13: read: `Test1-IS-OK': not a valid identifier

我需要这样的输出

LOG: Test1 and Test1-IS-OK
LOG: Test2 and Test2-IS-NOT
LOG: Test3 and Test3-IS-OK
LOG: Test4 and Test4-IS-OK
LOG: Test5 and Test5-IS-NOT

但代码不起作用。从文件中循环这 2 列的最佳方法是什么?有没有更简单的方法?

【问题讨论】:

  • FWIW 我投了赞成票,因为您有输入、输出、代码和问题陈述,但与 bash 标记相关的文本(将鼠标悬停在其上)明确表示 For shell scripts with errors/syntax errors, please check them with the shellcheck program (or in the web shellcheck server at https://shellcheck.net) before posting here.,如果您这样做了那么 shellcheck 会回答你的问题,而不是你必须在这里发布它,这样可能会导致你得到一些反对票。

标签: bash loops awk while-loop do-while


【解决方案1】:

最好避免使用 bash 并完全在 awk 中执行此操作。在 awk 中它很简单:

awk '{print "LOG:", $1, "and", $2}' file
LOG: Test1 and Test1-IS-OK
LOG: Test2 and Test2-IS-NOT
LOG: Test3 and Test3-IS-OK
LOG: Test4 and Test4-IS-OK
LOG: Test5 and Test5-IS-NOT

【讨论】:

  • 嘿,感谢有关 awk 的提示,但我更喜欢使用单独的变量,以便可以将该变量传递给其他语句。赞成你的答案
  • I prefer to use separate variable so I can pass that variable to other statement _ 可能是,如果你澄清这一点,那么我可以展示你如何在 awk 本身中做到这一点。
  • 对不起,我的意思是我需要使用变量 $a$b 并且需要在该循环内声明它们,因为该循环内的其他语句将使用此变量 $a$b。非常感谢您在这方面为我提供的帮助。其实@Hilton Fernandes 给出的答案就是我的意思。
  • 这个答案实际上非常容易出错并且是一种不好的做法。 Read this carefully
  • 确实,@anubhava ! awk 强大,gawk 更强大。可惜不是很常用。
【解决方案2】:

你的代码有什么问题?

a=$(awk '{ print $1 }' $output)

使用echo "a=${a}",您会看到,a 填充了所有行的输出。你试图找到一些函数,在 $a 之后调用。

while IFS=" " read -r $a $b

现在您正在尝试调用“函数”a 和 b。代码将在读取输入文件之前替换变量的值。当 a 填充“Test1 Test2”时,代码将尝试填充字段$Test1$Test2

当你只想改变输出,而不将变量传递给另一个语句时,你可以使用awk,或者

sed -E 's/([^ ]*) ([^ ]*).*/LOG: \1 and \2/' $output
# or
printf 'LOG: %s and %s\n' $(<$output)

在你的情况下,你可以让read读取两个参数:

while read -r a b 
do
    echo "LOG: $a and $b"
done < "$output"

【讨论】:

  • 谢谢,我接受了这个答案,因为它解释了我在代码中犯了什么错误,并且正如@anubhava 提到的那样,为了性能问题,awk 不需要在循环内
  • 我删除了我不正确的IFS=。请记住,在循环中使用awk 大部分时候都是错误的。
【解决方案3】:

请考虑将awk 解析转移到它所属的循环中:

#!/bin/bash

output="output.txt"

while read -r line 
do
    a=$(echo "${line}" | awk '{print $1}')
    b=$(echo "${line}" | awk '{print $2}')
    echo "LOG: $a and $b"
done < "$output"

根据@EdMorton 的好建议编辑

【讨论】:

  • awk 是一个循环文件​​的程序,所以它属于循环之外。
  • 确实,但是在脚本的第一个版本中,它不会根据需要生成每一行的字段,而是需要更多同步才能并行解析的两个字段列表。
  • 我为 while 循环添加了一个解决方案,而无需在循环内调用 awk
  • 如果您将该脚本复制/粘贴到shellcheck.net,它会告诉您一些但不是全部的问题。
【解决方案4】:

使用这个 Perl 单行代码:

perl -lane 'print "LOG: $F[0] and $F[1]";' output.txt > new.txt

Perl 单行程序使用这些命令行标志:
-e:告诉 Perl 查找内联代码,而不是在文件中。
-n:循环输入一行一次,默认将其分配给$_
-l:在执行内联代码之前剥离输入行分隔符(默认为 *NIX 上的"\n"),并在打印时附加它。-a : 在空格或-F 选项中指定的正则表达式上将$_ 拆分为数组@F

另请参阅:
perldoc perlrun: how to execute the Perl interpreter: command line switches

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-31
    • 2012-06-12
    相关资源
    最近更新 更多