awk ifs 和变量 - 不能将变量从一行传递到后续行

tcs*_*aru 4 awk

首先,我是 awk 的新手,所以如果它很简单,请原谅。

我正在尝试生成一个包含路径的文件。我为此使用了一个ls -LT清单以及一个 awk 脚本:

这是输入文件的示例:

vagrant@precise64:/vagrant$ cat structure-of-home.cnf

/home/:

vagrant

/home/vagrant:

postinstall.sh
Run Code Online (Sandbox Code Playgroud)

这将是预期的输出:

/home/vagrant
/home/vagrant/postinstall.sh
Run Code Online (Sandbox Code Playgroud)

awk 脚本应执行以下操作:

  1. 检查线路是否具有:在其
  2. 如果是,将字符串(没有:)分配给变量($path在我的情况下)
  3. 如果该行为空,则不打印任何内容
  4. 如果它不为空并且不包含:打印$path当前行$0

这是脚本:

BEGIN{
path=""
}
{
    if ($1 ~ /\:/)
        {
        sub(/\:/,"",$1)
        if (substr($1, length,1) ~ /\//)
            {
            path=$1;
            }
        else
            {
            path=$1"/"
            }
        }
    else if (length($0) == 0)
        {}
    else
        print $path$1
}
Run Code Online (Sandbox Code Playgroud)

问题是,当我运行脚本时,出现以下混乱:

vagrant@precise64:/vagrant$ awk -f format_output.awk structure-of-home.cnf
vagrantvagrant
postinstall.shpostinstall.sh
Run Code Online (Sandbox Code Playgroud)

请问我做错了什么?

msc*_*lli 5

正如taliezin指出的,您的错误是在打印时使用了扩展。与or不同,不使用 将变量名称扩展为其值,而是指行的字段(类似于)。$pathbashmakeawk$perl

因此,只需删除它即可使您的代码工作:

BEGIN{
path=""
}
{
    if ($1 ~ /\:/)
        {
        sub(/\:/,"",$1)
        if (substr($1, length,1) ~ /\//)
            {
            path=$1;
            }
        else
            {
            path=$1"/"
            }
        }
    else if (length($0) == 0)
        {}
    else
        print path$1
}
Run Code Online (Sandbox Code Playgroud)

然而,这并不是一个真正的awk解决方案:首先,不需要pathBEGIN规则中初始化,未定义的变量默认为""0,具体取决于上下文。

此外,任何awk脚本都由模式动作组成,前者说明when,后者说明要做什么。你有一个动作是总是执行(空模式),并在内部使用(嵌套的)条件语句来决定做什么。

我的解决方案如下所示:

# BEGIN is actually a pattern making the following rule run only once:
# That is, before any input is read.
BEGIN{
  # Split lines into chunks (fields) separated by ":".
  # This is done by setting the field separator (FS) variable accordingly:
# FS=":"  # this would split lines into fields by ":"

  # Additionally, if a field ends with "/",
  # we consider this part of the separator.
  # So fields should be split by a ":" that *might*
  # be predecessed by a "/".
  # This can be done using a regular expression (RE) FS:
  FS="/?:"  # "?" means "the previous character may occur 0 or 1 times"

  # When printing, we want to join the parts of the paths by "/".
  # That's the sole purpose of the output field separator (OFS) variable:
  OFS="/"
}

# First we want to identify records (i.e. in this [default] case: lines),
# that contain(ed) a ":".
# We can do that without any RE matching, since records are
# automatically split into fields separated by ":".
# So asking >>Does the current line contain a ":"?<< is now the same
# as asking >>Does the current record have more than 1 field?<<.
# Luckily (but not surprisingly), the number of fields (NF) variable
# keeps track of this:
NF>1{  # The follwoing action is run only if are >1 fields.

  # All we want to do in this case, is store everything up to the first ":",
  # without the potential final "/".
  # With our FS choice (see above), that's exactly the 1st field:
  path=$1
}

# The printing should be done only for non-empty lines not containing ":".
# In our case, that translates to a record that has neither 0 nor >1 fields:
NF==1{  # The following action is only run if there is exactly 1 field.

  # In this case, we want to print the path varible (no need for a "$" here)
  # followed by the current line, separated by a "/".
  # Since we defined the proper OFS, we can use "," to join output fields:
  print path,$1  # ($1==$0 since NF==1)
}
Run Code Online (Sandbox Code Playgroud)

就这样。删除所有注释,缩短变量名称并将[O]FS定义移动到命令行参数,您只需编写:

awk -F'/?:' -vOFS=\/ 'NF>1{p=$1}NF==1{print p,$1}' structure-of-home.cnf
Run Code Online (Sandbox Code Playgroud)