Remove section from file based on its content - unix-text-processing

How can I remove the config section that contains config B2 in the following file using bash? Any quick solution using sed or awk or similar? The different sections are separated by an empty line if that helps.
Input file:
section X
config A1
config A2
config A3
section Y
config A1
config B2
config A3
section Z
config C1
config C2
config A3
Expected output file:
section X
option A1
option A2
option A3
section Z
option C1
option C2
option C3

With awk:
awk 'BEGIN { RS="section" } $0 !~ /config B2/ { print RS$0 }' file
Set the record separator to "section" in the begin block. Then when the record doesn't contain "config B2", print the record separator followed by the record.

Related

concatenate within loop using sed

I have two types of files (A.n and B). A.n files are named A.1, A.2, A.3 etc.
In B file there is a word like 'Element.xyz' which I want to replace by 'Element_n.xyz' with all other things unchanged and append below the A.n files. The two added A.n and B files should be named as
final.n i.e. final.1, final.2, final.3 etc
So Final.1 file should look like this:
A.1
B file with Element.txt is replaced by Element_1.txt
I tried with this code but failed:
for f in A.*;
sed 's/Element.txt/Element_$f.txt/g' B >> tt;
cat A.$f tt >> final_$f.txt ;
done
Your code would append more and more data to the file tt. Your code would put the output from the B file before, not after, the text from the A file. Furthermore, the single quotes prevent the variable from being visible to sed. If I understand your question correctly, you are looking simply for
for f in A.*; do
n=${f#A.}
( cat "$f"
sed "s/Element.txt/Element_$n.txt/g" B ) >"final_$n".txt
done
The parentheses group the two commands so that their output can be redirected together at once. The file name in $f contains the A. part, so we chop it off with a parameter expansion and store that in $n. The argument to cat should obviously be the file name itself.

Powershell Simple Array output incorrect? [duplicate]

This question already has answers here:
PowerShell outputting array items when interpolating within double quotes
(3 answers)
Closed 8 months ago.
$nameonly = #("") * 20
$i=1
do
{
$nameonly[$i] = "A$i"
$i++
}
until ($i -eq 10)
write-host "$nameonly[4]"
When i run the code i expect the output to be: A4 but instead i get:
A1 A2 A3 A4 A5 A6 A7 A8 A9 [4]
`
Try removing the quotation marks in your final command.
So it should be write-host $nameonly[4]
This will get you the indexed value in position 4 of the array.
You can also skip the write-host command.
Invoking the variable $nameonly[4] will output to the console.
If you use quotation marks then it will convert the whole array into a string and output the entire array content as a string object.

Parse array based on variable and nth character

Looking to be able to parse an array based on a variable and take the next 2 characters
array=( 7501 7302 8403 9904 )
if var = 73, result desired is 02
if var = 75, result desired is 01
if var = 84, result desired is 03
if var = 99, result desired is 04
Sorry if this is an elementary question, but I've tried variations of cut and grep and cannot find the solution.
Any help is greatly appreciated.
You can use this search function using printf and awk:
srch() {
printf "%s\n" "${array[#]}" | awk -v s="$1" 'substr($1, 1, 2) == s{
print substr($1, 3)}' ;
}
Then use it as:
srch 75
01
srch 73
02
srch 84
03
srch 99
04
Since bash arrays are sparse, even in older versions of bash that don't have associative arrays (mapping arbitrary strings as keys), you could have a regular array that has keys only for numeric indexes that you wish to map. Consider the following code, which takes your input array and generates an output array of that form:
array=( 7501 7302 8403 9904 )
replacements=( ) # create an empty array to map source to dest
for arg in "${array[#]}"; do # for each entry in our array...
replacements[${arg:0:2}]=${arg:2} # map the first two characters to the remainder.
done
This will create an array that looks like (if you ran declare -p replacements after the above code to dump a description of the replacements variable):
# "declare -p replacements" will then print this description of the new array generated...
# ...by the code given above:
declare -a replacements='([73]="02" [75]="01" [84]="03" [99]="04")'
You can then trivially look up any entry in it as a constant-time operation that requires no external commands:
$ echo "${replacements[73]}"
02
...or iterate through the keys and associated values independently:
for key in "${!replacements[#]}"; do
value=${replacements[$key]}
echo "Key $key has value $value"
done
...which will emit:
Key 73 has value 02
Key 75 has value 01
Key 84 has value 03
Key 99 has value 04
Notes/References:
See the bash-hackers wiki on parameter expansion for understanding of the syntax used to slice the elements (${arg:0:2} and ${arg:2}).
See BashFAQ #5 or the BashGuide on arrays for more details on the syntax used above.

How to create a map of key:array in shell?

I want to create map in shell. Where each value is an array. So the map is key:array pair. For example it can be like this :
"Key1" : a1 a2 a3 a4
"key2" : b1 b2 b3
"key3" : c1
basically my code looks like this
listService(){
serviceType=$1
servicesList=($(getServices $serviceType))
}
listService serviceTypeA
listService serviceTypeB
listService serviceTypeC
here getServices is a function which returns an array of services based on the argument passed as $serviceType. So every time i call the listService function my serviceList gets overridden by new service list. But I want to keep all the services from different service type in form of a map like this :
"serviceA" : a1 a2 a3 a4
"serviceB" : b1 b2 b3
"serviceC" : c1
After that I want to access each array based on the key. How to achieve this.
Thanks in advance for your help.
Edit : I tried the answer provided by #cdarke . Here is my code now :
#!/bin/bash
declare -A arrayMap
getValues(){
key=$1
case $key in
AAA )
arr=( AA AAA AAAA )
;;
BBB )
arr=( BB BB BBBB )
;;
CCC )
arr=()
;;
esac
echo "${arr[#]}"
}
fillArrayMap(){
param=$1
values=( $(getValues $param) )
printf "\nIn $param\n"
echo -e "\nArray values is: ${values[#]}\n"
printf "\nLength of the array values is : ${#values[#]}\n"
arrayMap["$param"]=$values #THIS IS THE KEY LINE
valuesList=${arrayMap[$param]}
echo -e "\nArray valuesList is: ${valuesList[#]}\n"
printf "\nLength of the array valuesList is : ${#valuesList[#]}\n"
}
fillArrayMap AAA
fillArrayMap BBB
fillArrayMap CCC
Now from output I can see valuesList is getting only the first element of the values array. But I want valuesList to contain all the elements returned by the method getValues. i.e
valuesList= ${arrayMap[$param]}
now valuesList should contain all the elements, instead now it contains only 1 element. How to fix that ?
Note: My goal is to access each individual element like AAA or AA, I don't need it as a whole as a string like AA AAA AAAA
Bash does not support multi-dimensional arrays, but I don't think you need one. You can store a string in the form of a list in an array element, which will give you what you ask for.
# My made-up version of getServices
getServices() {
nm="$1"
last=${nm##*Type}
retn=(${last}1 ${last}2 ${last}3 ${last}4)
echo "${retn[#]}"
}
declare -A serviceList
listService(){
serviceType="$1"
# Here I use the key to make an assignment, which adds to the hash
serviceList["$serviceType"]=$(getServices $serviceType)
}
listService serviceTypeA
listService serviceTypeB
listService serviceTypeC
for key in ${!serviceList[#]}
do
echo "\"$key\": ${serviceList[$key]}"
done
Gives:
"serviceTypeC": C1 C2 C3 C4
"serviceTypeB": B1 B2 B3 B4
"serviceTypeA": A1 A2 A3 A4
EDIT for new question:
alter:
arrayMap["$param"]=$values # THIS IS THE KEY LINE
valuesList=${arrayMap[$param]}
to:
arrayMap["$param"]=${values[#]}
valuesList=( ${arrayMap[$param]} )
When you refer to an array variable by just it's name ($values) you only get the first element.
As cdarke already mentioned, bash arrays are one-dimensional. Over the years, folks have come up with ways to "fake" multi-dimensional arrays.
Two methods I've used are to maintain an array of array descriptions, or an array of pointers to other arrays. I'll answer with the former; the latter should be obvious if you want to explore on your own.
Here's a minimal example of array content getting used to populate variables:
#!/usr/bin/env bash
declare -A a=(
[b]='([0]="one" [1]="two")'
[c]='([0]="three" [1]="four")'
)
declare -p a
for key in ${!a[#]}; do
declare -a $key="${a[$key]}"
declare -p $key
done
Produces:
declare -A a=([b]="([0]=\"one\" [1]=\"two\")" [c]="([0]=\"three\" [1]=\"four\")" )
declare -a b=([0]="one" [1]="two")
declare -a c=([0]="three" [1]="four")
The critical bit here is that you're using declare to refer to the value of $key, since you can't just say $var="value" in bash.
Of course, you don't need to name your variables for the value of $key if you don't want to. Storing values in, say $value, would free you up to use special characters in $key.
An even simpler alternative, if it doesn't offend your sensibilities or restrict your key names too much, is to store the entire output of a declare -p command in the value of the array, and then eval it when you need it. For example:
declare -A a=(
[b]='declare -a b=([0]="one" [1]="two")'
[c]='declare -a c=([0]="three" [1]="four")'
)
for key in ${!a[#]}; do
eval "${a[$key]}"
done
Some people don't like eval. :-) It remains, however in your toolbox.
In your case, it's a little hard to advise because you haven't provided a full MCVE, but here's my contrived example.
#!/usr/bin/env bash
# contrived getServices function, since you didn't provide one
getServices() {
local -a value=()
local last="${1:$((${#1}-1)):1}" # last character of $1
for n in $( seq 1 $(( $RANDOM / 8192 + 1 )) ); do
value+=(${last}${n})
done
declare -p value # output of this function is actual bash code.
}
# populate the array
listService() {
servicesList[$1]=$( getServices $1 )
}
# Initialize this as empty to make `eval` safer
declare -A servicesList=()
# These services seem interesting.
listService serviceA
listService serviceB
listService serviceC
# Note that we're stepping through KEYS here, not values.
for row in "${!servicesList[#]}"; do
printf '"%s": ' "$row"
eval "${servicesList[$row]}" # Someone is bound to complain about this.
for column in "${!value[#]}"; do
# Add whatever $row and $column specific code you like here.
printf '%s ' "${value[$column]}"
done
printf "\n"
done
My output:
$ bash 2dimarrayexample
"serviceC": C1
"serviceB": B1 B2 B3 B4
"serviceA": A1 A2
Of course, your output may differ, since getServices produces random output. :)

Splitting strings in a text file and store to separate variables in shell

I have a text file consisting of strings as shown below. I require to take the last one, split the data and store it into separate variables for later use. I tried using | tr -s " " "\012" which worked but can't find a way to have the data stored in separate variables. Also, I would like to do this in shell. Any suggestions please?
Content of the text file:
324.0 0.4444 79
324.0 0.4445 80
324.0 0.4445 80
324.0 0.4445 80
...
326.0 0.5677 84 ... This is the line of interest
Thanks!
You can use read with sed:
# use sed to get the last line and read it into 3 variables
read -r var1 var2 var3 < <(sed '$!d' fff)
# check your variables
echo "var1=[$var1] var2=[$var2] var3=[$var3]"
Output:
var1=[326.0] var2=[0.5677] var3=[84]
You may do this
read var1 var2 var3 <<<"$(tail -n1 your_file_name)"
echo $var1 $var2 $var3 # testing for the desired result.

Resources