我们能在awk中使用shell变量吗?

77

我们是否可以在AWK中使用Shell变量,如$VAR,而不是$1$2?例如:

UL=(AKHIL:AKHIL_NEW,SWATHI:SWATHI_NEW)

NUSR=`echo ${UL[*]}|awk -F, '{print NF}'`
echo $NUSR
echo ${UL[*]}|awk -F, '{print $NUSR}'

我是一名Oracle数据库管理员,我们有很多导入请求。我正在尝试使用脚本自动化这个过程。该脚本将查找转储文件中的用户,并提示选择哪些用户需要加载转储文件。

假设转储文件中有两个用户AKHIL, SWATHI(转储文件中可能还有其他用户,我想导入更多的用户)。我想将这些转储文件导入到新用户AKHIL_NEWSWATHI_NEW。因此,输入应该类似于AKHIL:AKHIL_NEW,SWATHI:SWATHI_NEW

首先,我需要确定要创建的用户数量,然后从给定的输入中获取新用户,即AKHIL_NEW,SWATHI_NEW。这样我就可以连接到数据库并创建新用户,然后进行导入。我没有复制整个代码:我只复制了接受输入用户的部分代码。

UL=(AKHIL:AKHIL_NEW,SWATHI:SWATHI_NEW) ## it can be many users like     USER1:USER1_NEW,USER2_USER2_NEW,USER3:USER_NEW..

NUSR=`echo ${UL[*]}|awk -F, '{print NF}'` #finding  number of fields or users
y=1
while [ $y -le $NUSR ] ; do
    USER=`echo ${UL[*]}|awk -F, -v NUSR=$y  '{print $NUSR}' |awk -F: '{print $2}'` #getting     Users to created AKHIL_NEW and SWATHI_NEW and passing to SQLPLUS
    if [[ $USER = SCPO* ]]; then
        TBS=SCPODATA
    else
        if [[ $USER = WWF* ]]; then
            TBS=WWFDATA
        else
            if [[ $USER = STSC* ]]; then
                TBS=SCPODATA
            else
                if [[ $USER = CSM* ]]; then
                    TBS=CSMDATA
                else
                    if [[ $USER = TMM* ]]; then
                        TBS=TMDATA
                    else
                        if [[ $USER = IGP* ]]; then
                        TBS=IGPDATA
                        fi
                    fi
                fi
             fi
        fi
    fi

    sqlplus -s '/ as sysdba'  <<EOF   # CREATING the USERS in the database 
    CREATE USER $USER IDENTIFIED BY $USER  DEFAULT TABLESPACE $TBS TEMPORARY TABLESPACE TEMP QUOTA 0K on SYSTEM QUOTA UNLIMITED ON $TBS;

    GRANT
    CONNECT,
       CREATE TABLE,
       CREATE VIEW,
       CREATE SYNONYM,
       CREATE SEQUENCE,
       CREATE DATABASE LINK,
       RESOURCE,
       SELECT_CATALOG_ROLE
    to $USER;
    EOF 
    y=`expr $y + 1`
done

impdp sysem/manager DIRECTORY=DATA_PUMP DUMPFILE=imp.dp logfile=impdp.log SCHEMAS=AKHIL,SWATHI REMPA_SCHEMA=${UL[*]} 

在最后一个impdp命令中,我需要使用变量获取转储中的原始用户,即AKHIL、SWATHI。


1
问题是什么来着? - Thor
1
Shell提供了elif [[ $USER = WWF* ]]; then来避免条件超出页面右侧和大量的fi。这个脚本(目前)展示了为什么这是一个有价值的功能。 - Jonathan Leffler
可能是重复的问题:如何在awk脚本中使用shell变量 - tripleee
6个回答

130

是的,你可以在 awk 内部使用 shell 变量。有很多方法可以做到这一点,但我最喜欢的是使用 -v 标志定义变量:

是的,你可以在 awk 中使用 shell 变量。有很多方法可以实现它,但我最喜欢的是使用 -v 标志定义一个变量:

$ echo | awk -v my_var=4 '{print "My var is " my_var}'
My var is 4

只需将环境变量作为参数传递给-v标志即可。例如,如果您有这个变量:

$ VAR=3
$ echo $VAR
3

使用方法如下:

$ echo | awk -v env_var="$VAR" '{print "The value of VAR is " env_var}'
The value of VAR is 3
当然,您可以使用相同的名称,但是不需要$
$ echo | awk -v VAR="$VAR" '{print "The value of VAR is " VAR}'
The value of VAR is 3

关于awk中的$的说明:与bash、Perl、PHP等不同,它不是变量名的一部分,而是一个操作符


14
答案是否定的!你可以像将shell变量的值传递给C程序一样,将shell变量的值传递给awk脚本,但是你不能在awk脚本中访问shell变量,就像你不能在C程序中访问shell变量一样。与C语言一样,awk也不是shell。 - Ed Morton
4
你可以在 C 中访问环境变量。这就是它们最初设计的目的。 - Zenexer
1
@EdMorton "环境"在使用它的shell出现之前就已经存在了。Shell的创建部分原因是为了方便地操作环境。请参阅man 7 environ以获取非常简要的概述。 - Zenexer
1
@EdMorton 目标是从 shell 获取数据到 awk。使用环境变量是在启动时将字符串元数据传递给子进程的简单方法;一个简单的导出语句就可以实现这一点。当 shell 变量被导出时,它们就成为了环境变量,但两者之间的区别微不足道;即使是 bash 也会将它们视为相同的:[1](http://git.savannah.gnu.org/cgit/bash.git/tree/variables.c#n119),[2](http://git.savannah.gnu.org/cgit/bash.git/tree/variables.h#n82)我并不是想指出一个技术细节,而是你让事情变得不必要地复杂了。 - Zenexer
1
谢谢你,@brandizzi。我认为EM有点过于追求细节了。 - Frobozz
显示剩余3条评论

30
Awk和Gawk提供了ENVIRON关联数组来保存所有导出的环境变量。因此,在您的awk脚本中,如果VarName在运行awk之前已经被导出,您可以使用ENVIRON["VarName"]来获取VarName的值。
请注意,ENVIRON是预定义的awk变量,而不是shell环境变量。
由于我没有足够的声望评论其他答案,所以我必须在这里包含它们!
早期的答案显示$ENVIRON是错误的——那种语法会被shell扩展,并且可能会扩展为空。
更早的有关C无法访问环境变量的评论是错误的。与上面所说的相反,C(和C++)可以使用getenv("VarName")函数访问环境变量。许多其他语言提供类似的访问方法(例如Java:System.getenv(),Python:os.environ,Haskell System.Environment,…)。请注意,在所有情况下,对环境变量的访问都是只读的,您不能在程序中更改环境变量并将该值返回给调用脚本。

3
+1 但这是完全不正确的:在所有情况下,访问环境变量都是只读的。你可以更改环境,但仅适用于当前程序或由当前程序启动的程序。也就是说,你不能通过外部程序更改当前 shell 的环境。另一个注意点是,shell 变量与环境变量不同,尽管对于 shell 用户来说它们看起来相似,并且以相同的方式访问(至少在 sh 和类似的 shell 中)。 - akostadinov
另请参见 https://unix.stackexchange.com/questions/120788/pass-shell-variable-as-a-pattern-to-awk/120806#120806 - Dani_l
无法使其工作,变量似乎从未设置。TEST=xxx; echo | awk '{print "Test is " ENVIRON["TEST"]}' 仅打印 Test is - not2savvy

15

传递变量给 awk 有两种方式:一种方式是在命令行参数中定义变量:

$ echo ${UL[*]}|awk -F, -v NUSR=$NUSR '{print $NUSR}'
SWATHI:SWATHI_NEW

另一种方法是使用 export 将shell变量转换为环境变量,并从 ENVIRON 数组中读取环境变量:

$ export NUSR
$ echo ${UL[*]}|awk -F, '{print $ENVIRON["NUSR"]}'
SWATHI:SWATHI_NEW
更新于2016: OP拥有逗号分隔的数据,并希望提取给定索引处的项目。索引在shell变量NUSR中。NUSR的值传递给awkawk的美元符号操作符提取该项。
请注意,将UL声明为多个元素的数组并在bash中执行提取操作会更简单,而且完全不需要使用awk。但是,这种方法使用基于0的索引。
UL=(AKHIL:AKHIL_NEW SWATHI:SWATHI_NEW)
NUSR=1
echo ${UL[NUSR]} # prints SWATHI:SWATHI_NEW

2
第一个应该是“print NUSR”而不是“print NUSSR”,对吧? - Olivier Dulac
我对这个问题的理解是,OP希望程序执行“print $2”,这将打印第二个键值对,而不是打印数字2的“print 2”。 - Joni
2
有不止两种方法。请参见http://cfajohnson.com/shell/cus-faq-2.html#Q24中的comp.unix.shell FAQ第24个问题。 - Ed Morton
嗨Joni,你的回答非常有帮助。 - Akhil Chinnu
1
awk 中不使用 $ 来访问变量,而它是一个运算符。这并不是你想象中的那样。$ 只用于访问位置参数。 - Zenexer
显示剩余2条评论

9

还有另一种方法,但可能会造成极大的混淆:

$ VarName="howdy" ; echo | awk '{print "Just saying '$VarName'"}'
Just saying howdy
$

因此,您暂时退出单引号环境(通常会防止shell解释'$'),以解释变量,然后再回到该环境。 这种方法的优点是相对简洁。


这会造成任何潜在问题吗?目前看来似乎是最好的方式。 - Code42
首先,最重要的是:它很难懂。如果你把它放在一个 shell 脚本中,可能会导致阅读困难,而混淆可能会导致错误。其次,如果你把它放在一个更复杂的表达式中,你可能会发现自己需要转义引号和 $ 符号,这可能会使代码变得更加难以阅读。 - Philip Kearns
我曾使用过这个,虽然它很糟糕,如果你不经常使用像Bats这样的东西来测试脚本,确保引用地狱没有将你拖入但丁的第九层,就会导致疯狂。 - dragon788
@dragon788 没错 :) - Philip Kearns

2

不确定我是否理解了你的问题。

但是假设我们有一个变量number=3,我们想要在awk中使用它来代替$3,我们可以使用以下代码:

results="100 Mbits/sec 110 Mbits/sec 90 Mbits/sec"
number=3    
speed=$(echo $results | awk '{print '"\$${number}"'}')

因此,速度变量将获得值110。

希望这可以帮助您。


-1
不可以。你可以像将shell变量的值传递给C程序一样将其传递给awk脚本,但是你不能在awk脚本中访问shell变量,就像你不能在C程序中访问shell变量一样。与C语言一样,awk也不是shell。请参见cfajohnson.com/shell/cus-faq-2.html#Q24中的comp.unix.shell FAQ第24个问题。
编写代码的一种方法是:
UL="AKHIL:AKHIL_NEW,SWATHI:SWATHI_NEW"
NUSR=$(awk -F, -v ul="$UL" 'BEGIN{print gsub(FS,""); exit}')
echo "$NUSR"
echo "$UL" | awk -F, -v nusr="$NUSR" '{print $nusr}' # could have just done print $NF

但是由于您的原始起点:

UL=(AKHIL:AKHIL_NEW,SWATHI:SWATHI_NEW)

如果您将UL声明为仅具有一个条目的数组,那么您可能需要重新考虑您正在尝试做的任何事情,因为您可能完全错误的方法。

1
您可以从C程序中访问环境变量。这就是它们的设计目的:http://man7.org/linux/man-pages/man3/getenv.3.html 环境在技术上是操作系统的一个特性;shell只是大量利用它并将其暴露给用户。实际上,核心环境接口只能被本地(例如C)程序访问。当然,这一切都是基于POSIX的前提条件。 - Zenexer
既然你知道这么多,我就假设你也理解了我的观点,现在只是在扮演语言律师的角色(FYI,有一个非常好的新闻组comp.lang.c可以做到这一点)。当然,你可以调用函数(在C中使用getenv)或访问数组(在awk中使用ENVIRON)来获取shell变量的值,但你不能像在shell脚本中那样直接在C程序或awk脚本中使用该shell变量。 - Ed Morton
5
不,我不理解你试图表达的观点。你是不是想说不能直接使用$符号作为变量来访问shell变量?如果是这样的话,我认为那并不是问题的重点,那只是个技术细节。正如我们所看到的,无论是在awk还是C中,访问环境都是可能的,而且非常简单明了。 - Zenexer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接