【问题标题】:Shell file doesn't extract value properly [grep/cut] from file [bash]Shell 文件无法从文件 [bash] 中正确提取值 [grep/cut]
【发布时间】:2015-02-23 17:14:30
【问题描述】:

我有一个 test.txt 文件,其中包含键值对,就像任何其他属性文件一样。

test.txt

Name="ABC"
Age="24"
Place="xyz"

我想将不同键值的值提取到相应的变量中。为此,我编写了以下 shell 脚本

ma​​ster.sh

file=test.txt
while read line; do
  value1=`grep -i 'Name' $file|cut -f2 -d'=' $file`
  value2=`grep -i 'Age' $file|cut -f2 -d'=' $file`
done <$file 

但是当我执行它时;它运行不正常,给了我由命令的grep 部分提取的整行作为输出。有人可以指出错误吗?

【问题讨论】:

  • 为什么cut命令后面还有一个$file?离开它。您已经通过管道从 grep 命令中获得了指定的行。它可能看起来像 value1=`grep -i 'Name' $file|cut -d'=' -f2 注意 grep -i 命令。忽略大小写。如果字符组合年龄或姓名出现在一个值中,您的 grep 命令可能会返回 2 行。
  • 不,它只返回一行.. 但是在删除 $file 之后,我的问题仍然存在@user3387542
  • 你为什么不干脆source test.txt

标签: bash shell grep sh cut


【解决方案1】:

如果我正确理解了您的问题,下面的 Bash 脚本应该可以解决问题:

#!/bin/bash

IFS="="
while read k v ; do
    test -z "$k" && continue # skip empty lines
    declare $k=$v
done <test.txt

echo $Name
echo $Age
echo $Place

为什么会这样?大多数信息都可以从 bash 的手册页中检索到:

  • IFS 是“内部字段分隔符”,bash 的“读取”命令使用它来分隔每行中的字段。默认情况下,IFS 沿空格分隔,但重新定义为沿等号分隔。这是一个类似于 'cut' 命令的仅限 bash 的解决方案,您可以在其中将等号定义为分隔符 ('-d =')。
  • 'read' 内置函数从一行中读取两个字段。由于只提供了两个变量(k 和 v),第一个字段以 k 结尾,所有剩余字段(即等号之后)以 v 结尾。
  • 如注释所述,将跳过空行,即 k 变量为 emtpy (test -z) 的行。
  • 'eval' 也是一个 bash 内置函数,它执行参数(但仅在计算 $k=$v 之后),即 eval 语句等效于 Name="ABC" 等。
  • 'done' 之后的'
  • 三个“echo”语句只是为了表明该解决方案确实有效。

【讨论】:

  • 不要在这里使用evaldeclare "$k=$v" 做同样的事情而且更安全。
  • 为什么eval 不那么安全? @chepner
  • 我想我知道@chepner 的意思:'eval' 执行一个提供的参数,所以 $v 可能包含一些像rm -rf / 这样的 Bash 片段(它会被执行!)像 Age=rm -rf / 这样的输入行。如果使用'declare',它会简单地将字符串rm -rf / 分配给Age。更新我的解决方案...
【解决方案2】:

格式或文件是有效的 sh 语法,因此您可以直接获取文件:

source test.txt

在任何情况下,您的代码都不起作用,因为在管道之后您不应该再次指定文件。

value1=$(grep -i 'Name' "$file" | cut -f2 -d'=')

会保持你的逻辑

【讨论】:

  • 很好地观察了文件的格式。将解决方案缩减为单行,文件中嵌入的空白、cmets等不会造成问题。
【解决方案3】:

这是一条评论,但评论框不允许格式化。考虑重写:

while read line; do
  value1=`grep -i 'Name' $file|cut -f2 -d'=' $file`
  value2=`grep -i 'Age' $file|cut -f2 -d'=' $file`
done <$file 

作为:

while IFS== read key value; do
  case $key in
  Name|name) value1=$value;;
  Age|age) value2=$value;;
  esac;
done < $file

通过 cut 多次解析行是低效的。这与您的版本略有不同,因为比较区分大小写,但如有必要,很容易修复。例如,您可以预处理输入文件并将所有内容转换为小写。您可以即时进行预处理,但请注意,这会将您的 while 循环置于需要额外注意的子流程中(因为变量定义将以管道结束),但这并不重要。但是对于文件的每一行,通过 grep 运行整个文件两次是 O(n^2),而且可怕! (你为什么要阅读整个文件而不是仅仅回显该行?)

【讨论】:

  • 您发布的代码sn-p有多个语法错误。 while IFS== key value; do case $key in # double quotes not necessary; no field splitting Name|name) value1="$value";; Age|age) value2="$value";; done &lt; $file 并在更正这些之后......它不起作用。
  • @Subham 感谢您注意到明显的错误,但我很好奇您的评论“它不起作用”。我已经纠正了错误,效果很好。
  • 是的,我试过了,但我担心它不适合我!
  • 再次,它在什么方面不起作用?你有错误吗?或者您是否对字符串中存在双引号感到不安?还是……别的什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-25
  • 2015-05-09
  • 2011-12-23
  • 2017-01-05
  • 1970-01-01
  • 2023-04-09
相关资源
最近更新 更多