【问题标题】:Assigning command's output to shell variable and get the variables size将命令的输出分配给 shell 变量并获取变量大小
【发布时间】:2011-08-13 17:13:27
【问题描述】:

我有一个由数字组成的文件。通常,每一行都包含一个数字。我想计算文件中以数字“0”开头的行数。如果是这样的话,那我想做一些后期处理。

虽然我能够正确检索相应的行号,但检索到的总行数不正确。下面,我发布我正在使用的代码。

linesToRemove=$(awk '/^0/ { print NR; }' ${inputFile});
# linesToRemove=$(grep -n "^0" ${inputFile} | cut -d":" -f1);

linesNr=${#linesToRemove} # <- here, the error
# linesNr=${#linesToRemove[@]} # <- here, the error

if [ "${linesNr}" -gt "0" ]; then
    # do something here, e.g. remove corresponding lines.
    awk -v n=$linesToRemove 'NR == n {next} {print}' ${anotherFile} > ${outputFile}
fi

另外,对于基于 awk 的命令,我如何使用 shell 变量?我尝试了下面的命令,但它不能正常工作,因为 'myIndex' 被解释为文本而不是变量。

linesToRemove=$(awk -v myIndex="$myIndex" '/^myIndex/ { print NR;}' ${inputFile});

鉴于在${inputFile} 中找到以0 开头的行号,我想从${anotherFile} 中删除相应的行号。下面给出了 ${inputFile} 和 ${anotherFile} 的示例:

// ${inputFile}
0 
1
3
0

// ${anotherFile}
2.617300e+01 5.886700e+01 -1.894697e-01 1.251225e+02
5.707397e+01 2.214040e+02 8.607959e-02 1.229114e+02
1.725900e+01 1.734360e+02 -1.298053e-01 1.250318e+02
2.177940e+01 1.249531e+02 1.538853e-01 1.527150e+02

// ${outputFile}
5.707397e+01 2.214040e+02 8.607959e-02 1.229114e+02
1.725900e+01 1.734360e+02 -1.298053e-01 1.250318e+02

在上面的示例中,我需要从${anotherFile} 中删除行03,因为这些行对应于${inputFile} 中以0 开头的行。

【问题讨论】:

    标签: file shell awk grep


    【解决方案1】:

    一般来说,如果你这样做:

    linesToRemove=$(awk '/^0/ { print NR; }' ${inputFile});
    

    而不是这个:

    linesToRemove=$(awk '/^0/ { print NR; }' ${inputFile});
    linesNr=${#linesToRemove}
    

    使用这个:

    linesToRemove=$(awk '/^0/ { print NR; }' ${inputFile});
    linesNr=${echo $linesToRemove|awk '{print NF}'}
    

    POC:

    cat temp.sh
    #!/usr/bin/ksh
    
    lines=$(awk '/^d/{print NR}' script.sh)
    nooflines=$(echo $lines|awk '{print NF}')
    echo $nooflines
    torinoco!DBL:/oo_dgfqausr/test/dfqwrk12/vijay> temp.sh
    8
    torinoco!DBL:/oo_dgfqausr/test/dfqwrk12/vijay>
    

    【讨论】:

    【解决方案2】:

    我认为您必须执行以下操作才能分配数组:

    linesToRemove=( $(awk '/^0/ { print NR; }' ${inputFile}) )
    

    并且要获取元素的数量(正如您在注释行中所做的那样):

    linesNr=${#linesToRemove[@]}
    

    要从文件中删除行,您可以执行以下操作:

    sedCmd=""
    for lineNr in ${linesToRemove[@]}; do
      sedCmd="$sedCmd;${lineNr}d"
    done
    sed "$sedCmd" ${anotherFile} > ${outputFile}
    

    【讨论】:

    • 谢谢!通过在linesToRemove 数组中设置以0 开头的行号,我如何使用它从文件中删除这些行号?
    • @Javier:我使用d(删除)命令添加了sed 命令的示例。
    • 谢谢!有没有办法加快代码速度?我有几个文件要处理,并且只为一个文件执行上面的代码已经花费了一些时间。
    【解决方案3】:

    如果要统计文件中以0开头的行数,那么这一行是错误的。

    linesToRemove=$(awk '/^0/ { print NR; }' ${inputFile});

    上面说当行以0开头时打印行号,您的linesToRemove变量将包含所有行号,而不是总行数。使用END{} 块捕获总数。例如

    linesToRemove=$(awk '/^0/ {c++}END{print c}' ${inputFile});

    关于在 awk 中使用变量的第二个问题,请使用正则表达式运算符 ~。然后设置您的 myIndex 变量以包含 ^

    linesToRemove=$(awk -v myIndex="^$myIndex" '$0 ~ myIndex{ print NR;}' ${inputFile});

    最后,如果您只想删除那些以 0 开头的行,那么只需将其删除即可

    awk '/^0/{next}{print $0>FILENAME}' file 
    

    如果您想使用输入文件中捕获的内容从另一个文件中删除行,这是一种方法

    paste -d"|" inputfile anotherfile | awk '!/^0/{gsub(/^.*\|/,"");print}'
    

    或者只有一个awk 命令

    awk 'FNR==NR && /^0/{a[FNR]} NR>FNR && (!(FNR in a))' inputfile anotherfile
    

    粗略解释:FNR==NR && /^0/ 表示处理第一个文件整行以0开头并将其行号放入数组aNR&gt;FNR 表示处理下一个文件,如果行号不在数组中,则打印该行。请参阅 gawk 文档了解 FNR、NR 等的含义

    【讨论】:

    • @ghosdog74,谢谢!好吧,实际上最终目标是检索一个文件中以 0 开头的行,然后从另一个文件中删除这些对应的行。此外,不能使用变量“linesToRemove”的大小/长度来查找总行数吗?
    • @Javier,您应该发布这些文件的样本,并显示您的最终输出。我可以向您展示比您现在正在做的更好甚至更有效的方法
    • @ghosdog74,我更新了帖子并包含了这两个文件的一些示例。
    • @Javier,你一开始就应该这样做。查看我的编辑
    • @ghosdog74,通过使用一行awk 命令,输出与上面解释的不一样。它还显示inputfile 中的行号。与示例一样,我只想从anotherfile 中检索行,我确定inputfile 中没有以0 开头的行。
    【解决方案4】:

    这很大程度上取决于您正在进行的后期处理,但您真的需要实际计数吗?为什么不这样做:

    如果 grep ^0 $inputfile > /dev/null;然后 # 至少有一行以 0 开头 : 菲 grep -v ^0 $输入文件 |无前导零的工艺线 grep ^0 $输入文件 |带前导零的工艺线

    或者,甚至只是:

    如果 grep ^0 $输入文件 |带前导零的流程线;然后 # 一些后期处理 : 菲

    --编辑--

    根据您在评论中所说的话,我会推荐一种不同的方法。如果我理解正确,您想阅读文件 a,查找 ^0[0-9]* 形式的行, 然后从文件 b 中删除这些行号。如果文件变大,一次做一行会很慢。做吧:

    cmd=$( grep '^0[0-9]*$' a | sed 's/$/d;/g' ) sed "$cmd" b

    对 cmd 的赋值形成一个删除行的 sed 命令。在 b 上调用 sed 将省略这些行。您需要适当地重定向 sed 输出(可能到临时文件,然后返回到 b,或者如果您使用的是 gnu sed,则只需使用 'sed -i'。)

    【讨论】:

    • 谢谢!实际上,我需要做的是首先在文件A 中找到以0 开头的行,然后从文件B 中删除这些行。我更新了我的原始帖子并插入了我用来删除检索到的行的命令。
    • @javier。如果没有匹配的行,则 cmd 为空,最终 sed 为空操作,b 将保持不变。
    • 我收到以下消息:sed: -e expression #1, char 2: invalid usage of line address 0,在调用cmd=$( grep '^0[0-9]*$' ${inputFile} | sed 's/$/d;/g' ) 然后sed "$cmd" ${anotherFile} 之后
    • @Javier 在文件a中遇到“000”你想做什么?您可以通过将“| grep -v '^0*$'” 添加到构建 cmd 的管道中来跳过它,或者您可能想以不同的方式处理它。适当地构建命令; sed 不将第 0 行识别为有效地址。
    • 从其他 cmets 看来,您似乎并不关心文件 a 中行的内容,而只关心它出现的行号。在这种情况下,以不同的方式构建 cmd: cmd=$( grep -n ^0 a | sed 's/:.*/d;/g')
    【解决方案5】:

    鉴于对这个问题的大量修改,似乎最容易开始一个新的答案。你的问题可以用一个简单的单行来解决:

    $ sed "$( grep -n ^0 $inputFile | sed 's/:.*/d;/g' )" $anotherFile > $outputFile

    【讨论】:

      猜你喜欢
      • 2012-01-27
      • 2016-09-08
      • 2017-07-21
      • 2012-02-23
      • 2012-07-16
      • 2017-09-01
      • 1970-01-01
      • 2021-03-18
      • 1970-01-01
      相关资源
      最近更新 更多