Dereferencing variables in awk - variables

Why doesn't this work?
x=5
$ ls -l | awk '{print $(($x))}'
should print field 5 of ls -l command, right?

The only ways you should pass in the value of shell variable to awk are the following
$ x=5
$ ls -l | awk -v x="$x" '{print $x}'
$ ls -l | awk '{print $x}' x="$x"
The main difference between these two methods is that by using -v the value of x is set in the BEGIN block whilst the second method the value would not be set. All other methods with quoting tricks or escaping should not be used unless you like headaches.
However you don't want to being parsing ls at all, the command you really want is:
stat --printf="%s\n" *
Assuming the fifth column of your ls is the same as mine, this will display all the file sizes in the current directory.

You could access the shell variable something similar to these;
The first way is not suggested!
x=5
ls -l | awk '{print $'$x'}'
or assigning the value x to the variable shellVar, before execution of the program begins
x=5
ls -l | awk -v shellVar="$x" '{print $shellVar}'
or using an array containing the values of the current environment
export x=5
ls -l | awk '{print $ENVIRON["x"]}'

That's a shell variable, which is not expanded by the shell in single quotes. The reason we put awk scripts in single quotes is precisely to prevent the shell from interpreting things meant for awk's benefit and screwing things up, but sometimes you want the shell to interpret part of it.
For something like this, I prefer to pass the value in as an awk variable:
ls -l | awk -v "x=$x" '{print $x}'
but you could do any number of other ways. For instance, this:
ls -l | awk '{print $'$x'}'
which should really be this:
ls -l | awk '{print $'"$x"'}'
alternatively, this:
ls -l | awk "{print \$$x}"

Try this :
ls -l | awk '{print $'$x'}'

Related

Caret regexp produces no output in mawk

I am trying to print all files in /usr/bin/ where the filename starts with a v. This works,
ls -lA /usr/bin/ | awk '{print $9}' | grep ^v
Surprisingly, this returns no output,
ls -lA /usr/bin/ | awk '/^v/ {print $9}'.
I don't understand the difference. I am running Ubuntu 21.10 with awk -W version saying that it is on 1.3.4 20200120.
Edit: I understand that awk may not be the best way to accomplish what I am wanting to do here. But, this is an exercise in learning awk by testing my understanding via comparing it to the real output.
The difference between the two pipelines is that the first outputs the 9th column and then check to see if that starts with a v the second checks to see if the line starts with a v, change the second to:
$ ls -lA /usr/bin/ | awk '$9 ~ /^v/ {print $9}'
When writing:
/pattern/ { ... }
it's the same as writing
$0 ~ /pattern/ { ... }
but in your case you want to compare the 9th column, so write that instead.
But you really don't want to create a pipeline for this, and what would happen if your files contain a space?
You can consider using find or globs instead:
$ printf '%s\n' /usr/bin/v*
/usr/bin/vi
/usr/bin/view
...
or
$ find /usr/bin -name 'v*' -print
/usr/bin/vi
/usr/bin/view
...

Is using awk at least 'awk -F' always will be fine?

What is the difference on Ubuntu between awk and awk -F? For example to display the frequency of the cpu core 0 we use the command
cat /proc/cpuinfo | grep -i "^ cpu MHz" | awk -F ":" '{print $ 2}' | head -1
But why it uses awk -F? We could put awk without the -F and it would work of course (already tested).
Because without -F , we couldn't find from wath separator i will begin the calculation and print the right result. It's like a way to specify the kind of separator for this awk's using. Without it, it will choose the trivial separator in the line like if i type on the terminal: ps | grep xeyes | awk '{print $1}' ; in this case it will choose the space ' ' as a separator to print the first value: pid OF the process xeyes. I found it in https://www.shellunix.com/awk.html. Thanks for all.

ksh cmd one-liner to grep for several PIDs at once

I got a bunch of processes that I need to check CPU affinity for, so I got this one liner:
for i in `ps -Fae | grep proc_name| awk '{print $2}'`; do taskset -acp $i;done
but I have a problem, taskset shows all the child processes' pid too so I get a massive line of numbers along with their cpu affinity.
I want to pipe the above line into an egrep 'pid1|pid2' so I can filter out all the child processes.
I tried to this:
for i in `ps -Fae | grep proc_name| awk '{print $2}'`; do taskset -acp $i;done | xargs egrep 'ps -Fae | grep proc_name| awk '{print $2}''
but my ksh shell didn't like the awk brackets at all.
So I have two questions:
can taskset be changed to show only parent pid?
how do I write the last bit where I egrep only the parent pid?
Filter inside the loop:
for i in $(ps -Fae | grep proc_name| grep -v grep | awk '{print $2}'); do
taskset -acp "$i" | grep "$i"
done
It sounds like you're asking for this syntax if it were bash (see https://mywiki.wooledge.org/BashFAQ/001, I'm not sure what the equivalent robust read loop syntax is for ksh):
while IFS= read -r i; do
taskset -acp "$i"
done < <(ps -Fae | awk '/proc_name/{print $2}') |
grep -E 'pid1|pid2'
but that's pretty fragile, e.g. if pid1 appeared as a substring of some other pid. If you edit your question to provide concise, testable sample input (i.e. the output of ps -Fae and the associated output of taskset) plus the expected output then we can be of more help.

Using variable in sed as an output of previous command

Below is the output of my 'COMMAND' command .The output format is FILE:LINENO:PATTERN. I want to
take the below command output values in diff variables and use them in 'sed' command mentioned at the bottom.
<COMMAND>
./core.pkglist:16:package linux-release 6Server 9.0.3
./core.pkglist:18:package release-server 6Server 6.9.0.4.0.1.el6
./core.pkglist:32:package upstart 0.6.5 16.el6
./core.pkglist:33:package libnih 1.0.1 7.el6
I want to replace it with the command sed with inputs from the output of above command like:
sed "$var1 s/$var2/$c' $var3
that helps me run virtually the below command:
sed '16s/9.0.3/$c/' core.pkglist
1)The value 16 above should come from a variable like:
var1=$(COMMAND |awk -F':' '{print $2}')
2)9.0.3 should come from a variable.
var2=$(COMMAND |awk -F '{print $4}')
3)core.pkglist should come from a variable.
var3=$(COMMAND |awk -F '{print $1}')
4) $c is another command output
v3=$(echo Hello world); # Stupid examples of variables set from output commands
v1=$(wc -l <<< $v3);
v2=$(awk '{print $1}' <<< $v3);
c=$(echo Good morning);
sed "${v1}s/$v2/$c/" <<< $v3 # Use double quotes instead of singles
# echo $v3 | sed "${v1}s/$v2/$c/" works as well
Returns :
Good morning world

Trying to print awk variable

I am not much of an awk user, but after some Googling, determined it would work best for what I am trying to do...only problem is, I can't get it to work. I'm trying to print out the contents of sudoers while inserting the server name ($i) and a comma before the sudoers entry as I'm directing it to a .csv file.
egrep '^[aA-zZ]|^[%]' //$i/etc/sudoers | awk -v var="$i" '{print "$var," $0}' | tee -a $LOG
This is the output that I get:
$var,unixpvfn ALL = (root)NOPASSWD:/usr/bin/passwd
awk: no program given
Thanks in advance
egrep is superfluous here. Just awk:
awk -v var="$i" '/^[[:alpha:]%]/{print var","$0}' //"$i"/etc/sudoers | tee -a "$LOG"
Btw, you may also use sed:
sed "/^[[:alpha:]%]/s/^/${i},/" //"$i"/etc/sudoers | tee -a "$LOG"
You can save the grep and let awk do all the work:
awk -v svr="$i" '/^[aA-zZ%]/{print svr "," $0}' //$i/etc/sudoers
| tee -a $LOG
If you put things between "..", it means literal string, and variable won't be expanded in awk. Also, don't put $ before a variable, it will indicate the column, not the variable you meant.