Greping multiple lines from one multiline output Bash - bash

I have a command dumpsys power with this output:
POWER MANAGER (dumpsys power)
Power Manager State: mDirty=0x0
mWakefulness=Awake #
mWakefulnessChanging=false
mIsPowered=false
mPlugType=0
mBatteryLevel=67 #
mBatteryLevelWhenDreamStarted=0
mDockState=0
mStayOn=false #
mProximityPositive=false
mBootCompleted=true #
mSystemReady=true #
mHalAutoSuspendModeEnabled=false
mHalInteractiveModeEnabled=true
mWakeLockSummary=0x0
mUserActivitySummary=0x1
mRequestWaitForNegativeProximity=false
mSandmanScheduled=false
mSandmanSummoned=false
mLowPowerModeEnabled=false #
mBatteryLevelLow=false #
mLastWakeTime=134887327 (59454 ms ago) #
mLastSleepTime=134881809 (64972 ms ago) #
mLastUserActivityTime=134946670 (111 ms ago)
mLastUserActivityTimeNoChangeLights=134794061 (152720 ms ago)
mLastInteractivePowerHintTime=134946670 (111 ms ago)
mLastScreenBrightnessBoostTime=0 (134946781 ms ago)
mScreenBrightnessBoostInProgress=false
mDisplayReady=true #
mHoldingWakeLockSuspendBlocker=false
mHoldingDisplaySuspendBlocker=true
Settings and Configuration:
mDecoupleHalAutoSuspendModeFromDisplayConfig=false
mDecoupleHalInteractiveModeFromDisplayConfig=true
mWakeUpWhenPluggedOrUnpluggedConfig=true
mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig=false
mTheaterModeEnabled=false
mSuspendWhenScreenOffDueToProximityConfig=false
mDreamsSupportedConfig=true
mDreamsEnabledByDefaultConfig=true
mDreamsActivatedOnSleepByDefaultConfig=false
mDreamsActivatedOnDockByDefaultConfig=true
mDreamsEnabledOnBatteryConfig=false
mDreamsBatteryLevelMinimumWhenPoweredConfig=-1
mDreamsBatteryLevelMinimumWhenNotPoweredConfig=15
mDreamsBatteryLevelDrainCutoffConfig=5
mDreamsEnabledSetting=false
mDreamsActivateOnSleepSetting=false
mDreamsActivateOnDockSetting=true
mDozeAfterScreenOffConfig=true
mLowPowerModeSetting=false
mAutoLowPowerModeConfigured=false
mAutoLowPowerModeSnoozing=false
mMinimumScreenOffTimeoutConfig=10000
mMaximumScreenDimDurationConfig=7000
mMaximumScreenDimRatioConfig=0.20000005
mScreenOffTimeoutSetting=60000 #
mSleepTimeoutSetting=-1
mMaximumScreenOffTimeoutFromDeviceAdmin=2147483647 (enforced=false)
mStayOnWhilePluggedInSetting=0
mScreenBrightnessSetting=102
mScreenAutoBrightnessAdjustmentSetting=-1.0
mScreenBrightnessModeSetting=1
mScreenBrightnessOverrideFromWindowManager=-1
mUserActivityTimeoutOverrideFromWindowManager=-1
mTemporaryScreenBrightnessSettingOverride=-1
mTemporaryScreenAutoBrightnessAdjustmentSettingOverride=NaN
mDozeScreenStateOverrideFromDreamManager=0
mDozeScreenBrightnessOverrideFromDreamManager=-1
mScreenBrightnessSettingMinimum=10
mScreenBrightnessSettingMaximum=255
mScreenBrightnessSettingDefault=102
Sleep timeout: -1 ms
Screen off timeout: 60000 ms
Screen dim duration: 7000 ms
Wake Locks: size=0 Suspend Blockers: size=4
PowerManagerService.WakeLocks: ref count=0
PowerManagerService.Display: ref count=1
PowerManagerService.Broadcasts: ref count=0
PowerManagerService.WirelessChargerDetector: ref count=0
Display Power: state=ON #
I want to get the lines marked with # in a format of:
mScreenOffTimeoutSetting=60000
mDisplayReady=true
***
ScreenOfftimeoutSetting = 60000
DisplayReady = true
The commands output can vary from device to device and some of the lines might not be there or are in a different place. Thus if the searched line isn't there no errors should be generated.

It's not clear what you want. Aou can use sed to extract variables form the file and do whatever you want with them. Here's an example:
sed -n -e 's/^mSomeName=\(.*\)/newVariable=\1/p' -e 's/^mOtherName=.*+\(.*\)/newVariable2=\1/p' myFile
Explanation:
-n don't output anything per default
-e an expression follows. It's required since we have multiple expressions in place
s/^mSomeName=\(.*\)/newVariable=\1/p if file starts (^) with mSomeName= capture what follows (\(.*\)), replace the line with newVariable=\1, where \1 is what got captured, and print it out (p)
's/^mOtherName=.+(.)/newVariable2=\1/p' similar to the previous expression but will capture whatere comes after a + sign and print it behind newVariable2
This does something like:
$ sed -n -e 's/^mSomeName=\(.*\)/newVariable=\1/p' -e 's/^mOtherName=.*+\(.*\)/newVariable2=\1/p' <<<$'mSomeName=SomeValue\nmOtherName=OtherValue+Somethingelse'
newVariable=SomeValue
newVariable2=Somethingelse
<<<$'...' is a way of passing a string with linebreaks \n directly to the command in bash. You can replace it with a file. This command just outputs a string, nothing will get changed.
If you need them in bash variables use eval:
$ eval $(sed -n -e 's/^mSomeName=\(.*\)/newVariable=\1/p' -e 's/^mOtherName=.*+\(.*\)/newVariable2=\1/p' <<<$'mSomeName=SomeValue\nmOtherName=OtherValue+Somethingelse')
$ echo newVariable=$newVariable - newVariable2=$newVariable2
newVariable=SomeValue - newVariable2=Somethingelse
eval will execute the string which in this case set the variable values:
$ eval a=1
$ echo $a
1

If you want to just use Grep command, you can use -A (After) and -B (Before) options and pipes.
This is a exemple with 2 lines.
File test.txt :
test
aieauieaui
test
caieaieaipe
mSomeName=SomeValue
mOtherName=OtherValue+Somethingelse
nothing
blabla
mSomeName=SomeValue2
mOtherName=OtherValue+Somethingelse2
The command to use :
grep -A 1 'mSomeName' test.txt |grep -B 1 'mOtherName'
The output :
mSomeName=SomeValue
mOtherName=OtherValue+Somethingelse
--
mSomeName=SomeValue2
mOtherName=OtherValue+Somethingelse2

Related

Create a command on crontab that stream editor and rewrite a file every 15 minutes

Let's say I have a file with patterns to match into another file:
file_names.txt
pfg022G
pfg022T
pfg068T
pfg130T
pfg181G
pfg181T
pfg424G
pfg424T
I would like to use file_names.txt and use sed command into example.conf:
example.conf
{
"ExomeGermlineSingleSample.sample_and_unmapped_bams": {
"flowcell_unmapped_bams": ["/groups/cgsd/alexandre/gatk-workflows/src/ubam/pfg022G.unmapped.bam"],
"unmapped_bam_suffix": ".unmapped.bam",
"sample_name": "pfg022G",
"base_file_name": "pfg022G.GRCh38DH.target",
"final_gvcf_base_name": "pfg022G.GRCh38DH.target"
},
The sed command would replace pfg022G on example.conf with pfg022T, which is the next item in file_names.txt (sed s/pfg022G/pfg022T/). The example.conf at this point should look like this:
{
"ExomeGermlineSingleSample.sample_and_unmapped_bams": {
"flowcell_unmapped_bams": ["/groups/cgsd/alexandre/gatk-workflows/src/ubam/pfg022T.unmapped.bam"],
"unmapped_bam_suffix": ".unmapped.bam",
"sample_name": "pfg022T",
"base_file_name": "pfg022T.GRCh38DH.target",
"final_gvcf_base_name": "pfg022T.GRCh38DH.target"
},
After 15 minutes the substitution should be pfg022T to pfg068T and so on until all the items in file_names.txt are exhausted.
The following crontab would run your script every 15 minutes:
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7)
# | | | | |
# * * * * * command to be executed
15 * * * * /path/to/script
With script reading
#!/usr/bin/env sh
file1="file_names.txt"
file2="example.conf"
sed -i -e "$(awk '(NR>1){print "s/"p"/"$1"/g"}{p=$1}' $file1 | tac)" example.conf
The trick we use here is to do revere substitution. The file example.conf always contains only one string which is also in "file_names.txt". So if you attempt to substitute from the last to the front you will only do a single substitution.
We use awk here to build a sed-script and tac to reverse it so that we only have a single match:
$ awk '(NR>1){print "s/"p"/"$1"/g"}{p=$1}' $file_names.txt
s/pfg022G/pfg022T/g
s/pfg022T/pfg068T/g
s/pfg068T/pfg130T/g
s/pfg130T/pfg181G/g
s/pfg181G/pfg181T/g
s/pfg181T/pfg424G/g
s/pfg424G/pfg424T/g
If we do a sed with the above script, we will always end up with pfg424T (the last entry) as it will find a single match (assume we are in the third entry pfg068T), so sed will perform every substitution after that. However, when we reverse the order (using tac), sed will only find a single match.
For the logic of how i think this would work,
Create a cronjob, or if your server shuts down periodically create an anacron job, to run a bash script every 15 minutes.
In the bash script you can use an if statement you can test with grep with each line in filenames.txt which line exists in example.conf, and if that line exists to go onto the next line in filenames.txt. If you are at the last string in file_names.txt then the bash script should stop running with the exit command
You would run the sed command to replace your string. I do think the replace command should be able to replace this.
If you have to reload the service to load the amended configuration and then to add this also afterwards.
It might be easier to create a daemon/background process as opposed to a periodic cron job.
while read str;
do
sleep 900;
sed -ri "s#(^\"flowcell_unmapped_bams.*gatk-workflows/src/ubam/)(.*)(\.unmapped\.bam\"\],.*$)#\1$str\3#;s/(^\"sample.name.*: \")(.*)(\",.*$)/\1$str\3/;s/(^\"base_file_name.*: \")(.*)(\.GRCh38DH.*$)/\1$str\3/" example.conf;
done < file_names.txt &
Read the contents of file_names.txt line by line via a while loop, reading the line as a variable str. Sleep 900 seconds and then use this str variable in three sed commands. In all commands, enable regular expression interpretation with -r or -E and split the lines into three sections. Substitute the lines for sections 1, followed by the variable str and section 3. Add & at the end to run the process to the background.
I would perhaps just generate all the files in advance in a queue directory, and have the cron job pick up the next one on each invocation.
awk 'NR==FNR { a[++n] = $0; next }
{ file = $1 ".conf"
for(i=1; i<=n; i++) {
l = a[i]; sub("{{name}}", $0, l);
print l >file }
close file
}' - file_names.txt <<\____
{
"ExomeGermlineSingleSample.sample_and_unmapped_bams": {
"flowcell_unmapped_bams": ["/groups/cgsd/alexandre/gatk-workflows/src/ubam/{{name}}.unmapped.bam"],
"unmapped_bam_suffix": ".unmapped.bam",
"sample_name": "{{name}}",
"base_file_name": "{{name}}.GRCh38DH.target",
"final_gvcf_base_name": "{{name}}.GRCh38DH.target"
},
____
Running this on your sample file_names.txt creates the following files:
pfg022G.conf pfg068T.conf pfg181G.conf pfg424G.conf
pfg022T.conf pfg130T.conf pfg181T.conf pfg424T.conf
with contents like you would expect; here's pfg0222G.conf:
{
"ExomeGermlineSingleSample.sample_and_unmapped_bams": {
"flowcell_unmapped_bams": ["/groups/cgsd/alexandre/gatk-workflows/src/ubam/pfg022G.unmapped.bam"],
"unmapped_bam_suffix": ".unmapped.bam",
"sample_name": "pfg022G",
"base_file_name": "pfg022G.GRCh38DH.target",
"final_gvcf_base_name": "pfg022G.GRCh38DH.target"
},
Now, your cron job just needs to move one of these to example.conf and process it. When the directory with the files is empty, you are done.
#!/bin/sh
for f in confdir/*.conf; do
if [ -e "$f" ]; then
# Safeguard against clobbering previous run
if [ -e ./example.conf ]; then
echo "$0: example.conf is still there -- skipping this run" >&2
exit 63
fi
mv "$f" ./example.conf
exec your_main_script_or_whatever
# Should never fall through to here, but whatever
break
else
echo "$0: directory empty -- aborting" >&2
fi
done
To avoid a race condition -- if the previous cron job is still running, or failed for some reason, we don't want to be clobbering its input file. This requires your_main_script_or_whatever to remove example.conf when it completes. If you don't care about this, maybe you can simply remove the safeguard condition from the above script.

Loop Script from Input File

I have a reference file with device names in them. For example WABEL8499IPM101. I'm using this script to set the base name (without the last 3 digits) to look at the reference file and see what is already used. If 101 is used it will create a file for me with 102, 103 if I request 2 total. I'm looking to use an input file to run it multiple times. I'm also trying to figure out how to start at 101 if there isn't a name found when searching the reference file
I would like to loop this using an input file instead of manually entering bash test.sh WABEL8499IPM 2 each time. I would like to be able to build an input file of all the names that need compared and then output. It would also be nice that if there isn't a match that it starts creating names at WABEL8499IPM101 instead of just WABEL8499IPM1.
Input file example:
ColumnA (BASE NAME) ColumnB (QUANTITY)
WABEL8499IPM 2
Script:
SRCFILE="~/Desktop/deviceinfo.csv"
LOGDIR="~/Desktop/"
LOGFILE="$LOGDIR/DeviceNames.csv"
# base name, such as "WABEL8499IPM"
device_name=$1
# quantity, such as "2"
quantityNum=$2
# the largest in sequence, such as "WABEL8499IPM108"
max_sequence_name=$(cat $SRCFILE | grep -o -e "$device_name[0-9]*" | sort --reverse | head -n 1)
# extract the last 3digit number (such as "108") from max_sequence_name
max_sequence_num=$(echo $max_sequence_name | rev | cut -c 1-3 | rev)
# create new sequence_name
# such as ["WABEL8499IPM109", "WABEL8499IPM110"]
array_new_sequence_name=()
for i in $(seq 1 $quantityNum);
do
cnum=$((max_sequence_num + i))
array_new_sequence_name+=($(echo $device_name$cnum))
done
#CODE FOR CREATING OUTPUT FILE HERE
#for fn in ${array_new_sequence_name[#]}; do touch $fn; done;
# write log
for sqn in ${array_new_sequence_name[#]};
do
echo $sqn >> $LOGFILE
done
Usage:
bash test.sh WABEL8499IPM 2
Result in the log file:
WABEL8499IPM109
WABEL8499IPM110
Just wrap a loop around your code instead of assuming the args come in on the command line.
SRCFILE="~/Desktop/deviceinfo.csv"
LOGDIR="~/Desktop/"
LOGFILE="$LOGDIR/DeviceNames.csv"
while read device_name quantityNum
do max_sequence_name=$( grep -o -e "$device_name[0-9]*" $SRCFILE |
sort --reverse | head -n 1)
max_sequence_num=${max_sequence_name: -3}
array_new_sequence_name=()
for i in $(seq 1 $quantityNum)
do cnum=$((max_sequence_num + i))
array_new_sequence_name+=("$device_name$cnum")
done
for sqn in ${array_new_sequence_name[#]};
do echo $sqn >> $LOGFILE
done
done < input.file
I'd maybe pass the input file as the parameter now.

Continuously-updated (running-count) output from a program reading from a pipeline

How can I get continuously-updated output from a program that's reading from a pipeline? For example, let's say that this program were a version of wc:
$ ls | running_wc
So I'd like this to output instantly, e.g.
0 0 0
and then every time a new output line is received, it'd update again, e.g.
1 2 12
2 4 24
etc.
Of course my command isn't really ls, it's a process that slowly outputs data... I'd actually love to dynamically have it count matches and non matches, and sum this info up on a single line, e.g,
$ my_process | count_matches error
This would constantly update a single line of output with the matching and non matching counts, e.g.
$ my_process | count_matches error
0 5
then later on it might look like so, since it's found 2 matches and 10 non matching lines.
$ my_process | count_matches error
2 10
dd will print out statistics if it receives a SIGUSR1 signal, but neither wc nor grep does that. You'll need to re-implement them, more or less.
count_matches() {
local pattern=$1
local matches=0 nonmatches=0
local line
while IFS= read -r line; do
if [[ $line == *$pattern* ]]; then ((++matches)); else ((++nonmatches)); fi
printf '\r%s %s' "$matches" "$nonmatches"
done
printf '\n'
}
Printing a carriage return \r each time causes the printouts to overwrite each other.
Most programs will switch from line buffering to full buffering when used in a pipeline. Your slow-running program should flush its output after each line to ensure the results are available immediately. Or if you can't modify it, you can often use stdbuf -oL to force programs that use C stdio to line buffer stdout.
stdbuf -oL my_process | count_matches error
Using awk. First we create the "my_process":
$ for i in {1..10} ; do echo $i ; sleep 1 ; done # slowly prints lines
The match counter:
$ awk 'BEGIN {
print "match","miss" # print header
m=0 # reset match count
}
{
if($1~/(3|6)/) # match is a 3 or 6 (for this output)
m++ # increment match count
print m,NR-m # for each record output match / miss counts
}'
Running it:
$ for i in {1..10} ; do echo $i ; sleep 1 ; done | awk 'BEGIN{print "match","miss";m=0}{if($1~/(3|6)/)m++;print m,NR-m}'
match miss
0 1
0 2
1 2
1 3
1 4
2 4
2 5
2 6
2 7
2 8

How to properly use the grep command to grab and store integers?

I am currently building a bash script for class, and I am trying to use the grep command to grab the values from a simple calculator program and store them in the variables I assign, but I keep receiving a syntax error message when I try to run the script. Any advice on how to fix it? my script looks like this:
#!/bin/bash
addanwser=$(grep -o "num1 + num2" Lab9 -a 5 2)
echo "addanwser"
subanwser=$(grep -o "num1 - num2" Lab9 -s 10 15)
echo "subanwser"
multianwser=$(grep -o "num1 * num2" Lab9 -m 3 10)
echo "multianwser"
divanwser=$(grep -o "num1 / num2" Lab9 -d 100 4)
echo "divanwser"
modanwser=$(grep -o "num1 % num2" Lab9 -r 300 7)
echo "modawser"`
You want to grep the output of a command.
grep searches from either a file or standard input. So you can say either of these equivalent:
grep X file # 1. from a file
... things ... | grep X # 2. from stdin
grep X <<< "content" # 3. using here-strings
For this case, you want to use the last one, so that you execute the program and its output feeds grep directly:
grep <something> <<< "$(Lab9 -s 10 15)"
Which is the same as saying:
Lab9 -s 10 15 | grep <something>
So that grep will act on the output of your program. Since I don't know how Lab9 works, let's use a simple example with seq, that returns numbers from 5 to 15:
$ grep 5 <<< "$(seq 5 15)"
5
15
grep is usually used for finding matching lines of a text file. To actually grab a part of the matched line other tools such as awk are used.
Assuming the output looks like "num1 + num2 = 54" (i.e. fields are separated by space), this should do your job:
addanwser=$(Lab9 -a 5 2 | awk '{print $NF}')
echo "$addanwser"
Make sure you don't miss the '$' sign before addanwser when echo'ing it.
$NF selects the last field. You may select nth field using $n.

Concatenate output of two commands into one line

I have a very basic shell script here:
for file in Alt_moabit Book_arrival Door_flowers Leaving_laptop
do
for qp in 10 12 15 19 22 25 32 39 45 60
do
for i in 0 1
do
echo "$file\t$qp\t$i" >> psnr.txt
./command > $file-$qp-psnr.txt 2>> psnr.txt
done
done
done
command calculates some PSNR values and writes a detailed summary to a file for each combination of file, qp and i. That's fine.
The 2>> outputs one line of information that I really need. But when executed, I get:
Alt_moabit 10 0
total 47,8221 50,6329 50,1031
Alt_moabit 10 1
total 47,8408 49,9973 49,8197
Alt_moabit 12 0
total 47,0665 50,1457 49,6755
Alt_moabit 12 1
total 47,1193 49,4284 49,3476
What I want, however, is this:
Alt_moabit 10 0 total 47,8221 50,6329 50,1031
Alt_moabit 10 1 total 47,8408 49,9973 49,8197
Alt_moabit 12 0 total 47,0665 50,1457 49,6755
Alt_moabit 12 1 total 47,1193 49,4284 49,3476
How can I achieve that?
(Please feel free to change the title if you think there's a more appropriate one)
You could pass the -n option to your first echo command, so it doesn't output a newline.
As a quick demonstration, this :
echo "test : " ; echo "blah"
will get you :
test :
blah
With a newline between the two outputs.
While this, with a -n for the first echo :
echo -n "test : " ; echo "blah"
will get you the following output :
test : blah
Without any newline between the two output.
The (GNU version of) echo utility has a -n option to omit the trailing newline. Use that on your first echo. You'll probably have to put some space after the first line or before the second for readability.
You can use printf instead of echo, which is better for portability reasons.
printf is the correct way to solve your problem (+1 kurumi), but for completeness, you can also do:
echo "$file\t$qp\t$i $( ./command 2>&1 > $file-$qp-psnr.txt )" >> psnr.txt
While echo -n may work if you just want the print the output to console, it won't work if you want the output redirected to file.
If you want the concatenated output to be redirected to a file, this will work:
echo "Str1: `echo "Str2"`" >> file
I was also facing the same problem.
To define my problem, I have a script which is using the echo function like this:
echo -n "Some text here"
echo -n "Some text here"
The output I was getting is like this:
-n Some text here
-n Some text here
and I want the text to be in same line and it is also printing -n option in the output.
Note :- According to man Page, -n option do not print the trailing newline character.
The way I solved it using by adding the shebang in the starting of the script file like this.
#!/bin/bash
echo -n "Some text here"
echo -n "Some text here"
This will print the desired output like this:
Some text here Some text here
Hope this helps!

Resources