Bash命令根据我传递的参数读取一行 - 执行基于列的查找

时间:2017-02-17 04:03:20

标签: bash unix awk sed lookup

我有一个文件links.txt:

1 a.sh
3 b.sh
6 c.sh
4 d.sh

因此,如果我将1,4作为参数传递给另一个文件(master.sh),则应将a.sh和d.sh存储在变量中。

5 个答案:

答案 0 :(得分:2)

sed '3!d'会打印第3行,但不会打印以3开头的行。为此,您需要sed '/^3 /!d'。问题是你无法将它们组合成更多行,因为这意味着“删除所有不以3开头的内容”,这意味着所有其他行都将被遗漏。因此,请改用sed -n '/^3 /p',即默认情况下不打印并告诉sed要打印的行,而不是要删除的行。

你可以遍历参数并从它们创建一个打印行的sed脚本,然后使用这个输出运行sed

#!/bin/bash
file=$1
shift

for id in "$@" ; do
    echo "/^$id /p"
done | sed -nf- "$file"

script.sh filename 3 4运行。

如果要从输出中删除id,可以使用

cut -f2 -d' '

或者您可以修改生成的sed脚本来执行工作

echo "/^$id /s/.* //p"

即。仅在替换成功时打印。

答案 1 :(得分:1)

这会循环遍历每个参数并在链接文件中为它进行greps。结果通过管道传输到cut中,我们将分隔符指定为带-d标志的空格,使用-f标志将字段编号指定为2。最后,这将附加到名为files的数组中。

links="links.txt"
files=()

for arg in $@; do
    files=("${files[@]}" `grep "^$arg" "$links" | cut -d" " -f2`)
done;

echo ${files[@]}

用法:

$ ./master.sh 1 4
a.sh d.sh

编辑: 正如mklement0所指出的,上面的解决方案每个arg读取一次文件。下面首先构建模式,然后只读取一次文件。

links="links.txt"
pattern="^$1\s"
for arg in ${@:2}; do
    pattern+="|^$arg\s"
done
files=$(grep -E "$pattern" "$links" | cut -d" " -f2)
echo ${files[@]}

用法:

$ ./master.sh 1 4
a.sh d.sh

答案 2 :(得分:0)

以下 awk解决方案

  • 保留参数顺序;也就是说,结果反映了指定查找值的顺序(与文件中发生查找值的顺序相反)。

    • 如果这不重要(即,如果以 file 顺序输出结果是可接受的),下面的readarray技术可以与这个单行结合,这是一个通用的Panta's answer的变体:
      grep -f <(printf "^%s\n" "$@") links.txt | cut -d' ' -f2-
  • 表现良好,因为输入文件只能读取一次;唯一的要求是所有键值对都作为一个整体适合内存(作为单个关联的Awk数组(字典))。

  • 适用于没有嵌入空格的任何查找值

    • 同样,假设输出列值(包含示例输入中的a.sh等值)没有嵌入的空格。 awk无法很好地处理引用字段,因此需要更多工作。
#!/bin/bash

readarray -t files < <(
  awk -v idList="$*" '
    BEGIN { count=split(idList, idArr); for (i in idArr) idDict[idArr[i]]++ }
    $1 in idDict { idDict[$1] = $2 }
    END { for (i=1; i<=count; ++i) print idDict[idArr[i]] }
  ' links.txt
)

# Print results.
printf '%s\n' "${files[@]}"
  • readarray -t files将stdin输入(<)逐行读入数组变量files

    • 注意:readarray需要Bash v4 +;在Bash 3.x上,例如在macOS上,用。替换此部分 IFS=$'\n' read -d '' -ra files
  • <(...)是一个Bash process substitution,从松散的角度来说,它提供了封闭命令的输出,就好像它是(自行删除)临时文件一样。

    • 此技术允许readarray当前shell 中运行(与子shell 相反,如果已使用管道),这对于files变量保留在脚本的其余部分中。
  • awk命令按如下方式分解:

    • -v idList="$*"将所有命令行参数的空格分隔列表作为单个字符串传递给 Awk 变量idList

      • 请注意,这假设参数没有嵌入空格,这在这里确实是这种情况,通常也就是标识符的情况。
    • BEGIN { ... }仅在处理各行之前执行

      • split(idList, idArr)按空格将输入ID列表拆分为数组,并将结果存储在idArr中。

      • for (i in idArr) idDict[idArr[i]]++ }然后将(概念上常规的)数组转换为 associative 数组idDict(字典),其键是输入ID - 这样可以通过ID稍后,还允许为每个ID存储查找结果

    • 为每个输入行处理
    • $1 in idDict { idDict[$1] = $2 }

      • 如果行的第一个空格分隔字段($1 in idDict) - 例如$1 - 是关联数组的键(6),则模式in返回true {1}},如果是,则执行关联的操作(idDict)。
      • 操作{...}然后将第二个字段({ idDict[$1] = $2 }) - 例如$2 - 分配给密钥c.sh的{​​{1}}条目。
    • iDict执行一次 后处理所有输入行:

      • $1按顺序循环遍历所有输入ID 并打印每个ID的查找结果,即具有该ID的字典条目的

答案 3 :(得分:0)

以下是grep和cut的另一个例子:

#!/bin/bash
for line in $(grep "$1\|$2" links.txt|cut -d' ' -f2)
do
    echo $line
done

使用示例:

./master.sh 1 4
a.sh
d.sh

答案 4 :(得分:0)

为什么不只是存储值并随意调用它们:

items=()

while read -r num file
do
  items[num]="$file"
done<links.txt

for arg
do
  echo "${items[arg]}"
done

现在您可以随时使用items数组:)