ksh string manipulation $##!? - shell

So I have this spool file having this kind of content.
SQL> select file_name from dev_files;
FILE_NAME
------------------------------------------------------------------
file1.txt
file2.doc
file3.pdf
total.xls
4 rows selected.
SQL> spool off
I am writing a ksh script to load these files into a ftp server and update a log file. and I am stuck up bigtime. Here is the portion of my poor code after many tries.
dump="spoolfile.txt"
while read line;
do
if [[`expr match "$line" 'SQL'` !=3]] && [[`expr match "$line" 'FILE_NAME'` !=9]] && [[`expr match "$line" '---------'` !=9]]
then
ftp -inv $tgt_server <<EOT
quote user $uname
quote password $pword
mput $src_path/$line
quit
EOT
echo "sent $line" >> sent_files.log
fi
done < $dump
how do i ensure that "no rows selected" and say "4 rows selected." are not read? there can be any number instead of 4 corresponding to number of files. In the case of no files the spool file looks like this. the '.' is also missing.
SQL> select file_name from dev_files;
no rows selected
SQL> spool off

Here is a much more simple soltion: Instead of trying to find lines to ignore, mark the lines to process:
select concat("JHFDGFSH ",file_name) from dev_files
Now all you need to do is to ignore any line that doesn't start with JHFDGFSH.
[EDIT] If you can't do that: Start reading when you hit a line that's only ---- and stop at the first empty line. That should work unless Oracle starts to page.
And open an issue for upstream to tell them that their interface is brittle and that it will break eventually.
If that's OK, it's not your problem and not your fault when it breaks and production is down for a couple of days; just show the issue and say "see? You wanted it this way."
Also KSH is probably not the right tool; have a look at awk. This should work:
/^---+/,/^[ \t]*$/ { print; }
If you can't use awk, how about treating every line as a file name and ignore those which don't exist?
As long as no one creates a file named FILE_NAME, 4 rows selected. or SQL> spool off, you should be find as long as you make sure you properly quote all strings.

I agree with most of the comments about 'turn off headings' etc in the SQLPlus,
but this is Easy Peasy ;-)
while read line;
do
case ${line} in
-------* ) continue ;;
FILE_NAME ) continue ;;
SQL[>] ) continue ;;
*rows\ selected ) continue ;;
esac
# with enough case match targets, you can probably eliminate
# the if/fi completely. Good luck.
if [[`expr match "$line" 'SQL'` !=3]] .....
ftp ...
fi
done < $dump
You'll have to experiment with what values to put in the case statement to clean it up completely.
Also note that for any unusual characters, like '>', it is probably better to include them as a character class [>] (again requiring some experimentation on your part)
And finally, note that if you want to include white-space chars in the case match targets, either surround the phrase in dbl-quotes (") or escape each WS char like '\'.

Related

SED is giving me issues

I am working on a really basic script:
1) Grabs account keys from a text file (keyList.txt) --> key format looks like this: 1002000222,1002000400
2) For each key I am looping through and inserting them (using SED) into SQL queries held in another text file.
3) Query example:
UPSERT INTO ACCT_HIST (ACCT_KEY) SELECT ACCT_KEY FROM ACCT_HIST WHERE ACCT_KEY IN (101000033333) AND REC_ACTV_IND = 'Y' AND DT_KEY < 20191009;
My Bash snippet is below but to summarize the issue, SED is only replacing the values in the parenthesis one key at a time, rather than placing them both in the same parenthesis space. The below is now working perfectly.
#!/bin/bash
now=$(date +"%Y%m%d-%H:%M")
cp acct_transfer_soft_del_list.csv keyList_$now.txt
for key in $(<keyList_$now.txt)
do
sed "s/([^)]*)/(${key})/3" hbase.txt >> queries_$now.txt
done
hbase.txt holds the queries but I don't want to permanently change them, so I send the output to queries_$now.txt.
Please, note that you have IFS=,.
This is (probably) breaking your key with a unwanted behaviour.
I admit that I am not sure I understood entirely what you need, but I think you can use the first cycle in order to get everything you need.
Reusing your code, you can do something like this:
#!/bin/bash
now=$(date +"%Y%m%d-%H:%M")
IFS=","
while read f1 f2
do
echo "$f1,$f2"
sed "s/([^\)]*)/($f1,$f2)/3 " hbase.txt >> queries_$now.txt
done < acct_transfer_soft_del_list.csv > keyList_$now.txt
Anyway, I can't get straight your while cycle: it seems to do a simple copy of your file.
You could avoid it with cp acct_transfer_soft_del_list.csv keyList_$now.txt

Bash Script for loop with nested if statement

I have a script like this:
#!/bin/bash
x=${1:-20}
for ((i=1;i<=x;i++))
{
if ((i%3==0))
{
echo 'Fizz'
}
echo $i
}
I get an error color on the last brace in VIM and when I try to run the script I get a "syntax error near unexpected token" for that same brace. Without the nested if statement, this will print 1 through 20 on a new line for each number, which is the expected outcome. If the number is divisible by 3, it should print Fizz instead of that number. I'm not as worried about how to implement the replacement, that should be easy to figure out, but what I don't understand is why I cannot use a brace to close the for loop. If I take out the brace, I get an error that says end of file expected. So what is the proper syntax for ending a for loop with a nested if statement? I've looked around online and here on stack but haven't found a similar format to what I am trying to do. I don't like the
for f in *
format as it is not as easy to read for someone coming from a different coding language and I like to keep my code looking very similar across different languages (I use comments too, but just the same, I try to keep things as similar as possible which is why I used (( )) with the for loop.)
If I comment out the if statement and leave everything else intact, the error disappears and it will print
1
Fizz
2
Fizz
etc.
Any insight into this would be greatly appreciated. Thanks!
So here is what I was able to figure out thanks to #Cyrus:
x=${1:-20}
for ((i=1;i<=x;i++))
do
if ((i%3==0))
then
echo 'Fizz'
else
echo $i
fi
done
In many ways bash is simpler than most other languages but that makes it harder to work with when you are used to "higher" level languages.
So, to help out anyone else that's like me and just starting to code with bash, here is the full program I made, with comments as to why I coded it the way I did. If there are errors in my explanation or my formatting style, please point them out. Thanks! This was kind of fun to write, call me crazy.
# This will literally just print the string inside the single quotes on the screen
echo 'Try running this again but with something like this: fizzbuzz 25 pot kettle black'
# The $0 is the first index, in other words the file name of the executable,
# this will set the default value of x to 20 but will allow the user to input
# something else if they want.
x=${1:-20}
# This is the same but with string variables
f=${2:-FizzBuzz}
g=${3:-Fizz}
b=${4:-Buzz}
# We start the index variable at 1 because it's based on the input,
# otherwise it would echo 0 thru 19
for ((i=1;i<=x;1++))
do
# I recommend using (( )) for if statement arithmetic operations
# since the syntax is similar to other programming languages
if ((i%3==0 && i%5==0)); then echo $f
# you need to use a semicolon to separate if and then if they are
# on the same line, otherwise you can just go to the next line for
# your then statement
else if ((i%3==0)); then echo $g
else if ((i%5==0)); then echo $b
else echo $1
# You need fi in order to finish an if then statement
fi fi fi
done

Bash - Read config files and make changes in this file

i have config file like this for example:
# Blah blah, this is sample config file blah blah
# Something more blah blah
value1=YES
value2=something
#value3=boom
# Blah blah
valueN=4145
And i want to make script to read and edit config files like this. I thinking about make a menu with groups of config options, then after write an option console output will be like this:
Group of funny options (pick option to change value):
1. value1=YES
2. value2=something
3. [disabled]value3=boom
After picking 1 for exaple i can change value1 from YES to NO or disable and activate other (hash unhash) plus adding new variables to the end of file. Then in the end save all changes in this config file. Any tips what i need to use? Actually trying with read line + awk to skip # lines (with space), but still i have problem to get all this variables and making changes in config file. I will be grateful for your help.
Edit.
while read line
do
echo $line | awk '$1' != "#" && / / { print $1 $3 }'
done < config.conf
Thinking about this for now to read informations what i want. Plus i'm gonna use something like this to change values:
sed -c -i "s/("one" *= *).*/\1$two/" config.conf
I have completly no idea how i can get this variables to my script and use it like i write before. Actually i search for any tips, not someone who write this script for me. I'm beginner at linux scripting :V
I would recommend to abstain from such an, seemingly generic configuration program, because the comments might contain important informations about the current value and will be outdated, if the values change, while the comments don't.
Second problem is, that I would expect, if activating an entry is possible, deactivating it should be possible too. So now you have 2 options what to do with each value.
Third problem: In most cases, guessing a type by the value might work. YES seems to be a boolean, 47 an int, foobar a name - or is it a file? - but often a wider type is possible too, so YES can be just a string or a file, 47.3 might be valid where 47 is or might be not and so on.
However, for experimenting and trying things out, select and grep might be a start:
select line in $(grep "=" sample.conf) "write" "abort"
do
case $line in
"write") echo write; break ;;
"abort") echo abort; break ;;
'#'*=*) echo activate $line;;
*=[0-9]*) echo int value $line;;
*=YES|NO) echo boolean value $line;;
*) echo text value $line ;;
esac
done
Instead of 'echo intvalue $line' you would probably call a function "intconfigure" where only int values are accepted. For "write", you would write back to the file, but I omitted, conserving the comments without assignment and sorting them in again at the right position has to be done, which isn't trivial, given the opportunity to activate or deactivate comments.
But read up on the select command in shell and try it out and see how far you come.
If you think you have reached a usable solution, use this for all your configuration files privately and see, whether you prefer it over using a simple editor or not.

Need to pass string with newline character from Unix shell script to sql query

I am using unix shell KSH scripting to do some table cleanup.
I have a file "partner.txt" with 5000 line like this
>cat partner.txt
aaa0000
aaa0001
aaa0002
...
...
aaa5000
Using this file, I am supposed to clean few tables with matching, say agreements of the partners.
So i am constructing a partner list string in the format that i can use in the sql statement with 'IN' clause (select * from tab where partner IN partner_list)
('aaa0000',
'aaa0001','aaa0002',...,'aaa0010',
'aaa0011','aaa0012',...,'aaa0020',
...
'aaa4990','aaa4991',...,'aaa5000')
I am assigning the string to partner_list variable like this.
export BO="("
export BC=")"
export BQ="('"
export QC="','"
export QB="')"
export C=","
export CE=","'\n'
export QCE="',"'\n'"'"
partnerListLine=${BO}
while read partnerline;
do
if [ `expr ${counter} % 10` -eq 0 ]
then
partnerListLine=${partnerListLine}${partnerline}${CE}
elif [ ${counter} -lt ${numOfObsoletePartner} ]
then
partnerListLine=${partnerListLine}${partnerline}${C}
fi
counter=`expr ${counter} + 1`
done < partner.txt
partnerListLine=${partnerListLine}${partnerline}${BC}
Then I am using this partner list to fetch my agreement list like
SQL_agreement='select distinct a.agreement from partner_agreement_map a where a.partner in ${partner_list} order by agreement asc;'
I needed the newline character in my partner list since i was using sqlplus and was encountering SP2-0027: Input is too long (> 2499 characters)
I am adding the newline character by appending the below to my partner list string after N partners
CE=","'\n'
This worked fine when i was using sqlplus directly in the script.
But when i try to pass this partner_list string as parameter to a sql script, it shows '\n' in the query.
This is how i call my sql script and pass the parameter
sqlplus -s ${REFERENCE_DB_USER}/${REFERENCE_DB_PASS}#${DATABASE_INSTANCE} << !!
set serveroutput on size 10000;
set feedback off;
set verify off;
set echo off;
set term off;
set pagesize 0;
SET linesize 1000;
SET TRIMSPOOL ON;
spool 1_del_agreement_spool_$$.lst;
#1_del_agreement.sql ${partner_list};
spool off;
exit;
/
!!
this is my spooled file
>cat 1_del_agreement_spool_18165.lst <
select distinct a.agreement from partner_agreement_map a where a.partner in ('aaa0000',\n'aaa0001','aaa0002','aaa0003',...'aaa0010',\n'aaa0011'...) order by agreement asc
*
ERROR at line 1:
ORA-00907: missing right parenthesis
How can i maintain the newline character when i pass the parameter to the sql script and not have it replaced to '\n'?
I have tried ANSI-C quoting but failed.
Please let me know if you would need more details of the shell or sql script
UPDATED MY ENTIRE SOLUTION DESIGN
After trying all night, i have given up.
Thanks Aaron and mplf for your inputs.
I have decided to change my solution from file based to table based. I will be reading the partner.txt file and inserting the partners in a dummy temporary table. Then I can formulate queries with ease on other tables.
In fact, i think this should have been my first design :) There may be something very minor that i was missing in the previous design. But anyways, this will be much easier
I wish my team lead ever reviews design rather than code formatting issues :P
If you are using bash you can use $'\n' to print a newline character which would make your example
if [ `expr ${counter} % 10` -eq 0 ]
then
partnerListLine=${partnerListLine}${partnerline}"',"$'\n'"'"
else
...
Example:
$ echo hello"',"$'\n'"'"
hello',
'
I don't have a working solution but a hint: '\n' means "insert the literal backslash followed by n". So you tell the shell to leave this string alone.
Try NL=$(echo -e '\n') or similar to get a string variable which actually contains a newline. Then you can define CE=",$NL"
The shell might preserve this new line character as it processes the string.
Or use a tool like awk to create a string value with newlines which you assign to partner_list with partner_list=$(awk ...) to prevent the shell from doing any kind of processing of the value.
If that doesn't work, you may have to write the data to a file (with new lines).

replace $1 variable in file with 1-10000

I want to create 1000s of this one file.
All I need to replace in the file is one var
kitename = $1
But i want to do that 1000s of times to create 1000s of diff files.
I'm sure it involves a loop.
people answering people is more effective than google search!
thx
I'm not really sure what you are asking here, but the following will create 1000 files named filename.n containing 1 line each which is "kite name = n" for n = 1 to n = 1000
for i in {1..1000}
do
echo "kitename = $i" > filename.$i
done
If you have mysql installed, it comes with a lovely command line util called "replace" which replaces files in place across any number of files. Too few people know about this, given it exists on most linux boxen everywhere. Syntax is easy:
replace SEARCH_STRING REPLACEMENT -- targetfiles*
If you MUST use sed for this... that's okay too :) The syntax is similar:
sed -i.bak s/SEARCH_STRING/REPLACEMENT/g targetfile.txt
So if you're just using numbers, you'd use something like:
for a in {1..1000}
do
cp inputFile.html outputFile-$a.html
replace kitename $a -- outputFile-$a.html
done
This will produce a bunch of files "outputFile-1.html" through "outputFile-1000.html", with the word "kitename" replaced by the relevant number, inside the file.
But, if you want to read your lines from a file rather than generate them by magic, you might want something more like this (we're not using "for a in cat file" since that splits on words, and I'm assuming here you'd have maybe multi-word replacement strings that you'd want to put in:
cat kitenames.txt | while read -r a
do
cp inputFile.html "outputFile-$a.html"
replace kitename "$a" -- kitename-$a
done
This will produce a bunch of files like "outputFile-red kite.html" and "outputFile-kite with no string.html", which have the word "kitename" replaced by the relevant name, inside the file.

Resources