How to extract string of command result and use it in a loop - bash

Running a nx affected:apps command gives me this output:
> NX NOTE Affected criteria defaulted to --base=master --head=HEAD
> NX Affected apps:
- app-backend
- app-frontend
- app-something
- app-anything
I need to get all the application names and use them again for a command call.
So I started with that
output=$(nx affected:apps)
echo "$output" | grep -E "^\W+app-(\w+)"
This gives me
- app-backend
- app-frontend
- app-something
- app-anything
But I need to get the names only instead to run foo --name={appname} four times.
Also not quite sure how to use it in a loop. Quite new to bash scripting :-(

You may use -o (show matches only) with -P (perl regex moode) in gnu-grep:
nx affected:apps |
grep -oP "^\W+app-\K\w+" |
xargs -I {} docker build -t {} .
If gnu-grep isn't available then use this awk command:
nx affected:apps |
awk -F- '/app-/{print $3}' |
xargs -I {} docker build -t {} .

I don't have nx command here but you can try using xargs:
nx affected:apps | grep '^ -' | cut -d' ' -f4 | xargs -I{} echo docker build -t {} ./dist/{}
Remove echo to actually run the command.

You can use the --plain option:
nx affected:apps --plain
the command should return all the affected apps with space as a divider. You can then store those to a bash array and cycle through them in a for loop, running the command you need:
#!/bin/bash
AFFECTED=($(./node_modules/.bin/nx affected:apps --plain))
for t in ${AFFECTED[#]}; do
echo $t
done

Related

ansible grep plus awk with shell/command directive

I am trying run this ansible playbook
- name: Network Getting Started First Playbook Extended
gather_facts: false
hosts: localhost
tasks:
- name: Disable wolverine
shell: 'kubectl -n testns exec dashmpp-head-0 -c container -- bash -c "list versions | grep -w 20220928025228 | awk '{print $9}'"'
register: target_db2_version
failed_when: target_db2_version.stdout == "" or target_db2_version.stderr != ""
This keeps failing with:
shell: 'kubectl -n testns exec dashmpp-head-0 -c container -- bash -c "list versions | grep -w 20220928025228 | awk '{print $9}'"'
^ here
Problem here is with awk '{print $9}' part. If I remove that the command works fine.
Here is what all I have tried already
Tried running the command manually on shell prompt and it works fine
awk \'{print $9}\'
awk '{print \$9}'
awk \'\{print \$9\}\'
Tries using command directive instead of shell
You should use two single quotes to denote a literal single quote within a single-quoted string in YAML:
shell: 'kubectl -n testns exec dashmpp-head-0 -c container -- bash -c "list versions | grep -w 20220928025228 | awk ''{print $9}''"'
or in this case, there is no need to enclose the string in single quotes to begin with:
shell: kubectl -n testns exec dashmpp-head-0 -c container -- bash -c "list versions | grep -w 20220928025228 | awk '{print $9}'"

How to run a command like xargs on a grep output of a pipe of a previous xargs from a command in Bash

I'm trying to understand what's happening here out of curiosity, even though I can just copy and paste the output of the terminal to do what I need to do. The following command does not print anything.
ls /opt/local/var/macports/registry/portfiles -1 | sed 's/-.*//g' | sort -u | parallel "sudo port -N install" {} 2>&1 | grep -Po "Use '\K.*(?=')" | parallel "{}"
The directory I call ls on contains a bunch of filenames starting with the string I want to extract that ends at the first dash (so stringexample-4.2009 pipes stringexample into parallel (like xargs but to run each line separately). After running the command sudo port install <stringexample>, I get error outputs like so:
Unable to activate port <stringexample>. Use 'port -f activate <stringexample>' to force the activation.
Now, I wish to run port -f activate <stringexample>. However, I cannot seem to do anything with the output port -f activate gettext that I get to the terminal.
I cannot even do ... | grep -Po "Use '\K.*(?=')" | xargs echo or ... | grep -Po "Use '\K.*(?=')" >> commands_to_run.txt (the output stream to file only creates an empty file), despite the shorter part of the command:
ls /opt/local/var/macports/registry/portfiles -1 | sed 's/-.*//g' | sort -u | parallel "sudo port -N install {}" 2>&1 | grep -Po "Use '\K.*(?=')"
printing the commands to the terminal. Why does the pipe operator not work here? If the commands I wish to run are outputting to the terminal, surely there's got to be a way to capture them.

ssh remote command execution quoting and piping awk

I'm working on a script, that should find certain disks and add hostname to them.
I'm using this for 40 servers with a for loop in bash
#!/bin/bash
for i in myservers{1..40}
do ssh user#$i findmnt -o SIZE,TARGET -n -l |
grep '1.8T\|1.6T\|1.7T' |
sed 's/^[ \t]*//' |
cut -d ' ' -f 2 |
awk -v HOSTNAME=$HOSTNAME '{print HOSTNAME ":" $0}'; done |
tee sorted.log
can you help out with the quoting here? It looks like awk gets piped (hostname) from localhost, not the remote server.
Everything after the first pipe is running locally, not on the remote server.
Try quoting the entire pipeline to have it run on the remote server:
#!/bin/bash
for i in myservers{1..40}
do ssh user#$i "findmnt -o SIZE,TARGET -n -l |
sed 's/^[ \t]*//' |
cut -d ' ' -f 2 |
awk -v HOSTNAME=\$HOSTNAME '{print HOSTNAME \":\" \$0}'" ;
done | tee sorted.log
This is a shorter version of your stuff:
findmnt -o SIZE,TARGET -n -l |
awk -v HOSTNAME=$HOSTNAME '/M/{print HOSTNAME ":" $2}'
Applied to the above:
for i in myservers{1..40}
do ssh user#$i bash -c '
findmnt -o SIZE,TARGET -n -l |
awk -v HOSTNAME=$HOSTNAME '"'"'/M/{print HOSTNAME ":" $2}'"'"' '
done |
tee sorted.log
see: How to escape the single quote character in an ssh / remote bash command?

Scape quotes on remote command

I'm try to pass a commadn on remote server.
Command work fine on local server, but when try pass on remote server trought ssh get error for bad scpaing
ls -t /root/mysql/*.sql | awk 'NR>2 {system(\"rm \"" $0 \"\"")}'
Full comnand
ssh root#host -p XXX "mysqldump --opt --all-databases > /root/mysql/$(date +%Y%m%d%H%M%S).sql;ls -t /root/mysql/*.sql | awk 'NR>2 {system(\"rm \"" $0 \"\"")}'"
Actually no need to use awk and avoid all that quotes escaping:
ls -t /root/mysql/*.sql | tail -n +1 | xargs rm
This is assuming your *.sql files don't have any whitespaces otherwise you should use stat command and sort the output using sort.

Command composition in bash

So I have the equivalent of a list of files being output by another command, and it looks something like this:
http://somewhere.com/foo1.xml.gz
http://somewhere.com/foo2.xml.gz
...
I need to run the XML in each file through xmlstarlet, so I'm doing ... | xargs gzip -d | xmlstarlet ..., except I want xmlstarlet to be called once for each line going into gzip, not on all of the xml documents appended to each other. Is it possible to compose 'gzip -d' 'xmlstarlet ...', so that xargs will supply one argument to each of their composite functions?
Why not read your file and process each line separately in the shell? i.e.
fileList=/path/to/my/xmlFileList.txt
cat ${fileList} \
| while read fName ; do
gzip -d ${fName} | xmlstartlet > ${fName}.new
done
I hope this helps.
Although the right answer is the one suggested by shelter (+1), here is a one-liner "divertimento" providing that the input is the proposed by Andrey (a command that generates the list of urls) :-)
~$ eval $(command | awk '{a=a "wget -O - "$0" | gzip -d | xmlstartlet > $(basename "$0" .gz ).new; " } END {print a}')
It just generates a multi command line that does wget http://foo.xml.gz | gzip -d | xmlstartlet > $(basenname foo.xml.gz .gz).new for each of the urls in the input; after the resulting command is evaluated
Use GNU Parallel:
cat filelist | parallel 'zcat {} | xmlstarlet >{.}.out'
or if you want to include the fetching of urls:
cat urls | parallel 'wget -O - {} | zcat | xmlstarlet >{.}.out'
It is easy to read and you get the added benefit of having on job per CPU run in parallel. Watch the intro video to learn more: http://www.youtube.com/watch?v=OpaiGYxkSuQ
If xmlstarlet can operate on stdin instead of having to pass it a filename, then:
some command | xargs -i -n1 sh -c 'zcat "{}" | xmlstarlet options ...'
The xargs option -i means you can use the "{}" placeholder to indicate where the filename should go. Use -n 1 to indicate xargs should only one line at a time from its input.

Resources