使用awk打印单列或多列的功能

时间:2015-03-12 00:41:13

标签: bash shell unix awk

我经常使用awk来选择单列,在了解了别名之后我开始使用

alias a1='awk '\{print $1}'\'
alias a2='awk '\{print $2}'\'
...

在我学到更多东西后,我认为那些是俗气的,并用

代替
function a() {
    awk "{print \$$1}"
}

现在我可以执行a 3a 11而无需创建显式别名。

这样很好,但有时我需要选择多个列,当我这样做时,我不得不求助于输入实际的完整awk '{print ...}'命令(恐怖!)。

所以我试图找到一种类似于a函数的方法,但是会接受不同数量的参数,所以我可以做a 3或{{ 1}}或a 5 7

我已尝试使用a 2 4 9$@进行不同尝试,但无法正确使用我现在尝试的所有内容,我知道这些都是俗气的解决方法,所以我&# 39;而宁愿停下来问问如何以正确的方式去做。

谢谢大家。

2 个答案:

答案 0 :(得分:3)

你可能会因为这种事情而变得任意复杂(如果你想像a 2-5 7 11-一样想说cut怎么办?)但是这里有一个可以使用数字列表:

a() { (IFS=,; awk '{print '"${*/#/$}"'}'); }

这需要一些解释。

a() { ... }

定义了一个shell function,它以各种方式与别名不同,其中一个就是你可以给它参数。

在shell函数中,我想更改IFS的值;为了避免记住旧值并将其更改回来,我将实际上想要用(...)执行的命令包围起来,这使得它在子shell中执行。当子shell完成后,所有环境变化都会随之完成,因此它有效地将更改变为IFS本地。

IFS是用于word splitting的字符集,但它还定义了用于分隔"$*"扩展中元素的字符(即函数或脚本列表)当它被引号包围时。因此,将其设置为,表示$*展开将是以逗号分隔的列表。

我想要创建的awk程序实际上类似于{print $1,$4,$7},所以除了在列表之间添加逗号之外,我需要在每个数字之前添加$。我使用bash parameter expansion替代语法执行此操作:${parameter/pattern/replacement}。通过将*指定为参数,我得到$*,并将替换应用于每个参数。 (请注意,扩展是引用的。如果不是,它将无法工作。)

在替换表达式中,模式为空,因为模式开头的#字符表示匹配必须位于字符串的开头。由于实际模式为空,因此第一个匹配始终位于字符串的开头,因此将在每个参数的开头插入替换($)。需要#,因为//在语法上是不同的:它意味着"更改所有出现的模式"而不仅仅是第一个。

与许多语言不同,bash搜索和替换表达式不是以/结尾,而是以匹配的}结尾。如果您输入${p/foo/bar/},则会将foo的第一个实例替换为bar/

答案 1 :(得分:3)

$ cat tst.sh
function a {
    awk -v args="$*" '
        BEGIN { n=split(args,f) }
        { for (i=1;i<=n;i++) printf "%s%s", $(f[i]), (i<n?OFS:ORS) }
    '
}

echo "a b c d e f" | a 1 3 5
echo "---"
echo "a b c d e f" | a 1 3 4 6

$ ./tst.sh  
a c e
---
a c d f