Grep string if it has multiple match [closed] - bash

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
It was hard to formulate a question, better i will show example.
Txt file has these lines
city:state:address
city:state
city:
I need to extract strings where
a) only one occurrences of :
b) only one occurrences of : and has value after :
c) two occurrences of :
and put these strings to deferent files, so one file will contain all strings with
city:state:address second with city:state third one city:
Note: File has many such strings. Not obligatory to create three files in one command. It will be enough one command where i may define how many : string should contain.

Use these invocations of grep and pipe the output into different files:
grep -E "^[^:]+:\s*$" file.txt
grep -E "^[^:]+:[^:]+$" file.txt
grep -E "^[^:]+:[^:]+:.*$" file.txt
It looks for something that is not : with the regex [^:]+. It uses ^ and $ at the begin and end to match the whole input line.

This is a job for awk, not grep. All you need is:
awk -F':' '
NF==3 { print > "file_c"; next }
{ print > ($2=="" ? "file_a" : "file_b") }
' file
and that'll create all the files you want in one pass of your input file.
If you have more fields and more rules just write them all down so they're mutually exclusive, e.g. you could implement the above as:
NF==3 { print > "file_c" }
NF==2 && $2=="" { print > "file_a" }
NF==2 && $2!="" { print > "file_b" }

Related

Best way to alter a file in a bash script [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
i have a file that have variables and values
objective: open the file and replace all id by input id
[FILE]
var1 = 2
id = 3
var3 = 5
id = 12
var4 = 5
and i can't replace the id values to new ones.
here's my code, any help or something will help. thanks
#!/bin/bash
filename=$1
uuid=$2
input="./$filename"
# awk -v find="id " -v field="5" -v newval="abcd" 'BEGIN {FS=OFS="="} {if ($1 == find) $field=newval; print $1}' $input
while IFS= read -r line
do
awk -v find="id " -v field="5" -v newval="abcd" 'BEGIN {FS=OFS="="} {if ($1 == find) $field=newval;}' $input
echo $line
done < "$input"
expected output
execute
./myscript.sh file.cnf 77
expected output:
[FILE]
var1 = 2
id = 77
var3 = 5
id = 77
var4 = 5
I think sed is the right tool for this. You can even use its -i switch and update the file in-place.
$ cat file.txt
var1 = 2
id = 3
var3 = 5
id = 12
var4 = 5
$ NEW_ID=1234
$ sed -E "s/(id\s*=\s*)(.+)/\1${NEW_ID}/g" file.txt
var1 = 2
id = 1234
var3 = 5
id = 1234
var4 = 5
The string inside the quotes is a sed script for substituting some text with different text, and its general form is s/regexp/replacement/flags where "regexp" stands for "regular expression".
In the above example, the script looks for the string "id = ..." with any number of spaces or tabs around the "=" character. I divided the regexp into 2 groups (using parentheses) because we only want to replace the part to the right of the "=" character, and I don't think sed allows partial substitutions, so as a workaround I used \1 in the "replacement", which inserts the contents of the 1st group. The ${NEW_ID} actually gets evaluated by the shell so the value of the variable ("1234") is already part of the string by the time sed processes it. The g at the end stands for "global" and is probably redundant in this case. It makes sure that all occurrences of the regex on every line will get replaced; otherwise sed would only replace the first occurrence on each line.
Not sure. Bash scripts are extremely sensitive. I'm guessing your touch is what is causing this issue for a couple of reasons.
First whenever you touch a file name is should not consist of an operand or prefix unliss it is part of the shell script and $filename is shell or inline block quote. Touch is usually used for binaries or high priority data objects.
Second I'd try changing input and adjusted to $done and instead of echoing the $line echo the entire script using esac or end if instead of a do while loop.

How to delete text between two matching strings inclusive (only first occurence) that is at the very beginning of the file? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I am trying to remove the text between /* and */ that is at the beginning of the file. There can be white spaces or new lines (\n) before or in-between /* and */.
I tried following but doesn't work when space or new lines are there.
sed '/^\/\*/,/\*\//d' file
Sample file:
/*******
delete
bla
***
*/
/* do not */
print "hi"
/*******
dont delete
****/
Expected output:
/* do not */
print "hi"
/*******
dont delete
****/
If ed is available.
printf '%s\n' '/^[[:space:]]*\//;/^[[:space:]]*\*\//d' ,p Q | ed -s file.txt
Change Q to w to edit the file.
Using any awk in any shell on every UNIX box, this will produce the output you want from the input you provided:
$ awk 'f; /\*\//{f=1}' file
/* do not */
print "hi"
/*******
dont delete
****/
but consider also this more general approach:
$ cat tst.awk
{ rec = (NR>1 ? rec ORS : "") $0 }
END {
$0 = rec
gsub(/#/,"#A"); gsub(/{/,"#B"); gsub(/}/,"#C")
gsub(/\/\*/,"{"); gsub(/\*\//,"}")
sub(/^[[:space:]]*{[^}]*}[[:blank:]]*\n/,"")
gsub(/}/,"*/"); gsub(/{/,"/*")
gsub(/#C/,"}"); gsub(/#B/,"{"); gsub(/#A/,"#")
print
}
.
$ awk -f tst.awk file
/* do not */
print "hi"
/*******
dont delete
****/
See https://stackoverflow.com/a/56658441/1745001 for an explanation of what those gsub()s are doing.
awk 'BEGIN{f=-1} /^ *\/\*+/{f++}f' file
/* do not */
print "hi"
/*******
dont delete
****/

Bash-script won't work, initializing a variable (string) with output of sed [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I have some trouble with this script.
#!/bin/bash
i=1
for i in $(cat rows.txt);
do
j = "$(sed -ne "$[i-1]p" LOB_final.html)"
echo $j
sed -ne "$[i-1],$[i+4]p" LOB_final.html >> ./cards/$j.txt
done;
I have an other file that contains the right row-numbers (not exactly, [row-1] is the relevant row). This row contains a string with spaces included and should be the name of the file.
Script works so far as expected, but initializing j don't work.
Does anyone have a tip?
Thank you.
EDIT: The goal was, to write any six rows (1 before and 4 after ans the given row) in a file. The file should be named with the 1 before row of the given row (a string with white spaces included).
Questions is cleared, thanks to all.
check this, have fixed quite a few issues with your script
#!/bin/bash
i=1
for i in $(cat rows.txt);
do
j="$(sed -ne "$((i-1))p" LOB_final.html)"
echo "$j"
sed -ne "$((i-1)),$((i+4))p" LOB_final.html >> ./cards/"$j".txt
done;
Removed space which was there in assigning j
$[var] replaced with $((var))
Expanded variables in double quotes
You seem to want to get the filename from line i-1 and then to store that line together with the next five lines in a file with that name.
Using awk:
awk 'NR == FNR { rows[$0]; next }
FNR + 1 in rows { name = sprintf("./cards/%s.txt", $0); left = 6 }
left > 0 { print >name; --left }' rows.txt LOB_final.html
This has been tested on some toy data, but since I don't know what your data looks like I can't say for certain that it will work without (minor) modifications.
It has the benefit that it will not parse the whole of LOB_final.html twice for each row number read from rows.txt, which your original code does. In fact, it only ever reads each file once.

shell - remove numbers from a string column [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
SER1828-ZXC-A1-10002
SER1878-IOP-B1-98989
SER1930-QWE-A2-10301
SER1930-QWE-A2-10301
SER1930-QWS_GH-A2-10301
SER1930-REM_PH-A2-10301
From above data my requirement is to remove any number form eg "ZXC-A1"..
output required is
SER1828-ZXC-A-10002
SER1878-IOP-B-98989
SER1930-QWE-A-10301
SER1930-QWE-A-10301
SER1930-QWS_GH-A-10301
SER1930-REM_PH-A-10301
[I] have tried everything
... except, say:
awk -F- 'BEGIN { OFS="-" } { sub(/[0-9]/,"",$3); print }' yourdata
You should have been more clearer when you raise the problem. Do not add test cases later
You can try this, I have modified the third field to last but one. But credit to #Kaz
~> more test
SER1828-ZXC-A1-10002
SER1878-IOP-B1-98989
SER1930-QWE-A2-10301
SER1930-QWE-A2-10301
SER1930-QWS_GH-A2-10301
SER1930-REM_PH-A2-10301
SER1930-REM-SEW-PH-A2-10301
SER1940-REM-SPD-PL-D3-10301
~> awk -F- 'BEGIN { OFS="-" } { sub(/[0-9]/,"",$(NF-1)); print }' test
SER1828-ZXC-A-10002
SER1878-IOP-B-98989
SER1930-QWE-A-10301
SER1930-QWE-A-10301
SER1930-QWS_GH-A-10301
SER1930-REM_PH-A-10301
SER1930-REM-SEW-PH-A-10301
SER1940-REM-SPD-PL-D-10301
Edit
Explanation
-F- -> Define the input field separator
OFS="-" -> Set the output field separator
sub( -> Substitute
/[0-9]/ -> Select all the numbers
"" -> substitute with nothing
$(NF-1) -> For the last but one field
Print -> Print All results

Extracting the name from a line [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
A B 5 C Hello: XYZ 'Main Search String', Searching In: '[Name] = "I just want this" AND [State] = Active ('NAME', 'ACT')
How to extract this string: I just want this
grep "Main Search String" filename | awk -F"tab" '{print $6}' | ??
Using awk
awk -F\" '/Main Search String/ {print $2}' file
I just want this
using grep
grep -Po "\[Name\] = \"\K[^\"]*" file
grep -Po "(?<=\[Name\] = \")[^\"]*" file
sed -n 's/.*\[Name\] = "\([^"]*\)".*/\1/p' YourFile
assuming the content is betwween " and for the tag [Name] on any line of your input file
If you want more fun :)
$ cat /tmp/delme
A B 5 C Hello: XYZ 'Main Search String', Searching In: '[Name] = "I just want this" AND [State] = Active ('NAME', 'ACT')
$ cat /tmp/delme | perl -e 'while (<>) { if ( /.*[Name] = \"(.*)\"/ ) { print "$1\n"; } };'
I just want this
With this you can use the whole regex and pattern match things provided by perl...and perl is on almost every system as I know.Give it a try! ;)
EDITED for pattern found case:
$ cat /tmp/delme | perl -e 'while (<>) { print "TEXT: ";print; if ( /.*Main Search String.*\[Name] = "(.*)\"/ ) { print "RESULT: $1\n"; } };'
TEXT: A B 5 C Hello: XYZ 'Main Search String', Searching In: '[Name] = "I just want this" AND [State] = Active ('NAME', 'ACT')
RESULT: I just want this
TEXT: A B 5 C Hello: XYZ 'Not Search String', Searching In: '[Name] = "I just want this" AND [State] = Active ('NAME', 'ACT')
Got it in 13 characters plus the file name:
cut -d\" -f 2 text.txt
This command cuts the text into columns separated by the double-quotes. (-d\") That creates three columns. It prints out the second column with your answer (-f 2).
Like Lutz Horn said, you need to provide a bigger data set to test any of these commands. Many of them will fail if they do not represent the complete pattern they need to parse.

Resources