csv 如何将多行grep输出提供给xargs,每行包含多个参数

368yc8dk  于 2023-03-27  发布在  其他
关注(0)|答案(2)|浏览(159)

我有一个CSV文件grep,其中的列数可能会有所不同,例如:

$ grep -v 'Coba' ETFs.csv | grep 'Sector'
CAPE,Ossiam Shiller Barclays Cape Europe Sector Value TR,EUPE,FWB
EUPB1,Ossiam Shiller Barclays Cape Global Sector Value,EUPB,FWB

对于grep的每一个输出行,我需要运行一个Python脚本,并提交带有-t标志的列#3和带有-n标志的列#2。为了简单起见,让我们假设脚本打印提交的参数。因此,在接收到上面的输出时,脚本应该像这样执行两次:

script.py -t EUPE -n Ossiam Shiller Barclays Cape Europe Sector Value TR
script.py -t EUPB -n Ossiam Shiller Barclays Cape Global Sector Value

我的问题是xargs将整个grep输出作为一个参数处理。预期的行为是逐行处理输入,按逗号分割行内容并以任何期望的顺序将其提交给脚本。如何做到这一点?
Python脚本示例:

import argparse, sys

aparser = argparse.ArgumentParser()
aparser.add_argument('-t', '--ticker', required=True)
aparser.add_argument('-n', '--name')

args = aparser.parse_args()
print(f"START {sys.argv[0]} for {args.ticker} {args.name}: ", end='')

更新23.3

目前的做法是只提交一个参数作为代码标志-t,因为-n名称标志是可选的。然而,真实的情况是一次提交多个标志,例如ETF代码和名称,筛选器,交易所等。

$ grep -v 'Coba' ETFs.csv | grep 'Sector' | cut -d ',' -f3 | xargs -I {} python script.py -t {}

正如评注中指出的,它也可以用awk表示。

$ awk -F, '! /Coba/ && /Sector/ {print $3}' ETFs.csv | xargs -I {} python script.py -t {}

@SHERLOCK建议的解决方案其实看起来很有逻辑,我们提取了同一个输入的不同部分,但是第二次参数提交不起作用,导致argparse使用提醒。

awk -F, '! /Coba/ && /Sector/ {printf "%s,%s\n", $3, $2}' ETFs.csv | xargs -t -I {} bash -c 'python script.py -t $(echo "{}" | awk -F "," "{print \$1}") -n $(echo "{}" | awk -F "," "{print \$2}")'

rm -rf这样的有害代码注入在这里不应该引起关注,因为我总是从检查CSV输出开始,然后才将其传输到脚本。

voase2hg

voase2hg1#

BashFAQ #1while read循环比xargs更好:

while IFS=, read -r col3 col2 _ <&3; do
    python script.py -t "$col3" -n "$col2"
done 3< <(awk -F, '! /Coba/ && /Sector/ {printf "%s,%s\n", $3, $2}' ETFs.csv)

这样我们就不会在每一行都启动一个单独的shell,而只是使用同一个shell,它已经在Python的awk的stdout上运行了脚本循环。因此,安全问题和引用错误都得到了解决。
一些注意事项:

  • 使用文件描述符3而不是默认的fd 0(stdin)进行重定向会使stdin空闲,因此script.py可以使用它来提示用户,而不会无意中从ETFs.py阅读数据。
  • col2之后的_导致后续字段被丢弃(放入变量_中,这是脚本中用于忽略垃圾的常规方法),而不是附加到col 2中的值。
  • 请参阅BashFAQ #24以了解为什么使用while read ...; done < <(awk ...)而不是awk ... | while read ...; done
  • 如果awk用于过滤的行集不是很大,你可以完全摆脱awk,使用bash进行过滤:
while IFS=, read -r col1 col2 col3 _ <&3; do
   [[ $col2 = *Coba* ]] && continue   # skip lines with Coba in description
   [[ $col2 = *Sector* ]] || continue # skip lines without Sector in description
   python script.py -t "$col3" -n "$col2"
done 3<ETFs.csv
  • 问题中给出的原始xargs方法中最明显的bug(除了被描述为通过人工审查解决的安全问题之外)缺少命令替换周围的引号--"$(...)",而不仅仅是裸$(...),防止将结果字符串中的单词拆分为单独的参数(然后根据当前工作目录中的文件系统内容展开为globs)。因为第二列中的ETF描述包含空格,所以这对该数据至关重要。
2mbi3lxu

2mbi3lxu2#

您可以使用'-d'标志和xargs将分隔符指定为',',并使用'-I'标志为输入字符串指定占位符。之后,您可以使用'awk'从每行中提取所需的列,并将它们作为参数传递给Python脚本。
以下是您可以执行的操作:

grep -v 'Coba' ETFs.csv | grep 'Sector' | xargs -d',' -I {} sh -c 'script.py -t $(echo "{}" | awk -F"," "{print \$3}") -n "$(echo "{}" | awk -F"," "{print \$2}")"'

这将用','分割每一个输入行,然后使用awk提取第三列和第二列,并将它们作为参数传递给script.py。第二列用双引号括起来,以处理包含空格的情况。
您可能需要根据您的shell和CSV文件的确切格式稍微修改该命令。

相关问题