Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unix 'alias' fails with 'awk' command

Tags:

linux

shell

awk

I'm creating an alias in Unix and have found that the following command fails..

alias logspace='find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9 }''

I get the following :

awk: cmd. line:1: {print
awk: cmd. line:1:       ^ unexpected newline or end of string

Any ideas on why the piped awk command fails...

Thanks, Shaun.

like image 986
Boardman411 Avatar asked Jun 16 '14 14:06

Boardman411


People also ask

What does awk '{ print $2 }' mean?

awk '{ print $2; }' prints the second field of each line. This field happens to be the process ID from the ps aux output.

What is awk '{ print $4 }'?

The AWK language is useful for manipulation of data files, text retrieval and processing. -F <value> - tells awk what field separator to use. In your case, -F: means that the separator is : (colon). '{print $4}' means print the fourth field (the fields being separated by : ).

What is awk '{ print $1 }'?

If you notice awk 'print $1' prints first word of each line. If you use $3, it will print 3rd word of each line.

What does $1 $2 indicate in awk file?

The awk variables $1 or $2 through $nn represent the fields of each record and should not be confused with shell variables that use the same style of names. Inside an awk script $1 refers to field 1 of a record; $2 to field 2 of a record.


2 Answers

To complement's @Dropout's helpful answer:

tl;dr

The problem is the OP's attempt to use ' inside a '-enclosed (single-quoted) string.

The most robust solution in this case is to replace each interior ' with '\'' (sic):

alias logspace='find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | 
                awk '\''{print $5, $9 }'\'''
  • Bourne-like (POSIX-compatible) shells do not support using ' chars inside single-quoted ('...'-enclosed) strings AT ALL - not even with escaping.
    • (By contrast, you CAN escape " inside a double-quoted string as \", and, as in @Droput's answer, you can directly, embed ' chars. there, but see below for pitfalls.)
  • The solution above effectively builds the string from multiple, single-quoted strings into which literal ' chars. - escaped outside the single-quoted strings as \' - are spliced in.
    Another way of putting it, as @Etan Reisinger has done in a comment: '\'' means: "close string", "escape single quote", "start new string".
  • When defining an alias, you usually want single quotes around its definition so as to delay evaluation of the command until the alias is invoked.

Other solutions and their pitfalls:

The following discusses alternative solutions, based on the following alias:

alias foo='echo A '\''*'\'' is born at $(date)'

Note how the * is effectively enclosed in single quotes - using above technique - so as to prevent pathname expansion when the alias is invoked later.

When invoked, this alias prints literal A * star is born, followed by the then-current date and time, e.g.: A * is born at Mon Jun 16 11:33:19 EDT 2014.


Use a feature called ANSI C quoting with shells that support it: bash, ksh, zsh

ANSI C-quoted strings, which are enclosed in $'...', DO allow escaping embedded ' chars. as \':

alias foo=$'echo A \'*\' is born at $(date)'

Pitfalls:

  • This feature is not part of POSIX.
  • By design, escape sequences such as \n, \t, ... are interpreted, too (in fact, that's the purpose of the feature).

Use of alternating quoting styles, as in @Dropout's answer:

Pitfall:

'...' and "..." have different semantics, so substituting one for the other can have unintended side-effects:

alias foo="echo A '*' is born at $(date)" # DOES NOT WORK AS INTENDED

While syntactically correct, this will NOT work as intended, because the use of double quotes causes the shell to expand the command substitution $(date) right away, and thus hardwires the date and time at the time of the alias definition into the alias.

As stated: When defining an alias, you usually want single quotes around its definition so as to delay evaluation of the command until the alias is invoked.


Finally, a caveat:

The tricky thing in a Bourne-like shell environment is that embedding ' inside a single-quoted string sometimes - falsely - APPEARS to work (instead of generating a syntax error, as in the question), when it instead does something different:

 alias foo='echo a '*' is born at $(date)'  # DOES NOT WORK AS EXPECTED.

This definition is accepted (no syntax error), but won't work as expected - the right-hand side of the definition is effectively parsed as 3 strings - 'echo a ', *, and ' is born at $(date)', which, due to how the shell parses string (merging adjacent strings, quote removal), results in the following, single, literal string: a * is born at $(date). Since the * is unquoted in the resulting alias definition, it will expand to a list of all file/directory names in the current directory (pathname expansion) when the alias is invoked.

like image 182
mklement0 Avatar answered Oct 17 '22 14:10

mklement0


You chould use different quotes for surrounding the whole text and for inner strings.

Try changing it to

alias logspace="find /apps/ /opt/ -type f -size +100M -exec ls -lh {} \; | awk '{print $5, $9 }'"

In other words, your outer quotes should be different than the inner ones, so they don't mix.

like image 38
Dropout Avatar answered Oct 17 '22 16:10

Dropout