As pointed out by taliezin, your mistake was to use $
to expand path
when printing. Unlike bash
or make
, awk
doesn't use the $
to expand variables names to their value, but to refer to the fields of a line (similar to perl
).
So just removing this will make your code work:
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
}
However, this is not really an awk
ish solution:
First of all, there is no need to initialize path
in a BEGIN
rule, non-defined variables default to ""
or 0
, depending on context.
Also, any awk
script consist of patterns and actions, the former stating when, the latter what to do.
You have one action that's always executed (empty pattern), and internally uses (nested) conditionals to decide what to do.
My solution would look like this:
# 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)
}
And that's all. Removing all the comments, shortening the variable name and moving the [O]FS
definitions to command line arguments, all you have to write is:
awk -F'/?:' -vOFS=\/ 'NF>1{p=$1}NF==1{print p,$1}' structure-of-home.cnf
Best Answer
You can read from
/dev/tty
or from/dev/stdin
./dev/tty
is pretty ubiquitous (even one the very few, along with/dev/null
and/dev/console
to be required by POSIX),/dev/stdin
is less common, but at least GNU awk would recognize it as meaning stdin even if the system doesn't have such a device/special file.On Linux (and Cygwin, but not other Unix-likes), reading from
/dev/stdin
is not the same as reading from stdin (fd 0), but instead means reading from the same file as open on fd 0. If that file is a regular file for instance, that would start reading from the start of the file instead of where fd 0 currently points to in the file.Because GNU
awk
handles paths like/dev/stdin
by itself (and treat it as reading from stdin), on Linux or Cygwin it would behave differently from other applications that don't do that special handling. If you wanted the Linux/Cygwin behaviour ingawk
(which here is likely not your case), you could either use///dev/stdin
or/dev/./stdin
forgawk
to stop recognising it as the/dev/stdin
special case, or probably better here if you intend to rely on Linux/Cygwin specific behaviour, use the Linux/Cygwin specific/proc/self/fd/0
path (which/dev/stdin
actually symlinks to on those systems).