1

I need to assign a command to a variable and execute it with variable in linux bash. The command executes fine from command line but not from the variable because of multiple quotes. I hope backslash should be used for multiple quotes to avoid the error. Please help with the correct format.

Code:

parm="cat file|awk '$1>0 && $1="abc" {print f $0} {f=$0 ORS}'"
eval "$parm"

Contents:

abc
123
efg
456

Error:

awk: cmd. line:1: >0 && =abc {print f bash} {f=bash ORS}
awk: cmd. line:1: ^ syntax error

Thanks in advance!

0

1 Answer 1

1

To store code, use functions, not variables:

parm() {
  < file awk '
    $1 > 0 {$1 = "abc"; print f $0}
    {f = $0 ORS}'
}
parm

If you had to use variables:

parm='
  < file awk '\''
    $1 > 0 {$1 = "abc"; print f $0}
    {f = $0 ORS}'\''
'
eval "$parm"

Is probably the easiest where we only use single quotes, and to insert single quotes, we get out of the quotes and quote the single quote with \'.

See also:

parm=$(cat << 'EOF'
  < file awk '
    $1 > 0 {$1 = "abc"; print f $0}
    {f = $0 ORS}'
EOF
)
eval "$parm"

When the delimiter is quoted in << 'delimiter', then there's no $param, $(( arith )), `cmdsubst`, $(cmdsubst) nor \ line continuation happening inside here documents which makes it easy to write any multiline text containing anything.

I've replaced cat file | (makes little sense to concatenate a single file) with < file, moved $1 = "abc" assignment out of the condition where it was confusing and added a separator (here a newline which also improves legibility) between the two awk statements as required by POSIX.

Using functions also means that it can get parametrised as in:

parm() {
  < "$1" awk '
    $1 > 0 {$1 = "abc"; print f $0}
    {f = $0 ORS}'
}
parm file

Where the first argument is made the stdin of awk or:

parm() {
  cat -- "$@" | awk '
    $1 > 0 {$1 = "abc"; print f $0}
    {f = $0 ORS}'
}
echo -1 3 | parm file1 - file2

Where all the arguments are taken as file paths (or stdin if -¹ or no argument is supplied) and concatenated and the result fed as awk's stdin via a pipe or:

parm() {
  awk -- '
    $1 > 0 {$1 = new; print f $0}
    {f = $0 ORS}' new=abc "$@"
}
echo -1 3 | parm file1 - new=xyz file2

Where all the arguments are passed to awk which can take them as file paths, stdin for -¹ (or if there's no file path argument) or awk variable assignments² ³.


¹ use ./- for the file literally called - in the current working directory

² use ./foo=bar.txt for a file literally called foo=bar.txt in the current working directory.

³ Beware that in GNU awk aka gawk, new=@/text/ is interpreted as new=text with text taken as a regexp. new='\@/text/' works around it but issues a warning, a better approach at working around that misfeature is to call awk as POSIXLY_CORRECT=1 awk ... which disables that misfeature for gawk and should be harmless for other awk implementations.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.