Bash script not recognizing existing file - bash

I have to write a simple bash script, which does the compilation of multiple sql scripts which can contain recursive references to the other sql scripts with Oracle SQLPlus convention, so in the end the result will be only one SQL file, containing all statements from all (possibly also recursively) referenced subscripts.
I have created a simple script called oracle-script-compressor.sh with following bash code:
#!/bin/bash
#
#
function err () {
echo $* > "/dev/stderr"
}
function replace_subscripts_placeholders() {
local PROCESSING_FILENAME=$1
echo "-- processing file $PROCESSING_FILENAME"
while read script_line; do
local script_line_NO_LEAD_SPACE="$(echo -e "${script_line}" | sed -e 's/^[[:space:]]*//')"
if [[ $script_line_NO_LEAD_SPACE == \#\#* ]] ; then
local file_name="./${script_line_NO_LEAD_SPACE#\#\#}"
echo "-- found reference to file $file_name"
echo "-- starting with processing, pwd=$PWD"
# for debug purposes:
ls -la
# but this returns always false:
if [ -f "$file_name" ]; then
# so this part is never started:
. replace_subscripts_placeholders $file_name
else
err_msg="WARNING: Could not find the referenced file $file_name"
echo "-- $err_msg"
err $err_msg
fi
else
echo "$script_line"
fi
done < $PROCESSING_FILENAME
}
if test -z "$1" ; then
err "Usage: oracle-script-compressor.sh {file_name_to_process} [> output_file]"
err " Be aware, if the referenced files within {file_name_to_process} are not specified by absolute paths, you have to start the script from corresponding directory."
err " If the part [> output_file] is omitted, then this script writes to standard output"
err " If the part [>> output_file] is used, then the result will be appended to output_file it it exists before the processing"
else
if [ -f "$1" ]; then
replace_subscripts_placeholders $1
else
echo "file $1 does not exist"
fi
fi
and I am stuck on interesting problem - it seems, inside of the function replace_subscripts_placeholders() the file check
if [ -f "$file_name" ]; then
. replace_subscripts_placeholders $file_name
else
err_msg="WARNING: Could not find the referenced file $file_name"
echo "-- $err_msg"
err $err_msg
fi
never goes to the recursion call and even, if I remove the if statement and really call the function in recursion with the correct referenced file name, which exists, it is still not recognized to be found and not passed into the loop over all lines of the referenced file in the recursive call (then the error file not found comes and the loop cannot be executed).
I have added the debug messages into script like mentioned above in the script, but still I am unable to find, why the hell should bash not find the file, if it is in the same directory. The scripts are placed in
user#mycomputer:/tmp/test$ ls -la
celkem 52
drwxr-xr-x 2 user user 4096 zář 14 21:47 .
drwxrwxrwt 38 root root 36864 zář 14 21:48 ..
-rw-r--r-- 1 user user 51 zář 14 21:45 a.sql
-rw-r--r-- 1 user user 51 zář 14 21:45 b.sql
-rw-r--r-- 1 user user 590 zář 14 21:46 start.sql
user#mycomputer:/tmp/test$
and the content of the file start.sql looks like this:
spool output__UpdSch.log
whenever sqlerror exit sql.sqlcode
--
--
PROMPT a.sql - starting
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as START_TIMESTAMP from dual;
##a.sql
PROMPT a.sql - finished
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as FINISH_TIMESTAMP from dual;
--
--
PROMPT b.sql - starting
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as START_TIMESTAMP from dual;
##b.sql
PROMPT b.sql - finished
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as FINISH_TIMESTAMP from dual;
--
--
spool off
and if I execute the script, it seems it decoded the filenames correctly, but there is still the problem in bash - the testing of the file existence returns always false:
user#mycomputer:/tmp/test$ ~/tmp/oracle-script-compressor.sh start.sql
-- processing file start.sql
spool output__UpdSch.log
whenever sqlerror exit sql.sqlcode
--
--
PROMPT a.sql - starting
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as START_TIMESTAMP from dual;
-- found reference to file ./a.sql
-- starting with processing, pwd=/tmp/test
celkem 52
drwxr-xr-x 2 user user 4096 zář 14 21:47 .
drwxrwxrwt 38 root root 36864 zář 14 21:48 ..
-rw-r--r-- 1 user user 51 zář 14 21:45 a.sql
-rw-r--r-- 1 user user 51 zář 14 21:45 b.sql
-rw-r--r-- 1 user user 590 zář 14 21:46 start.sql
-- WARNING: Could not find the referenced file ./a.sql
WARNING: Could not find the referenced file ./a.sql
PROMPT a.sql - finished
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as FINISH_TIMESTAMP from dual;
--
--
PROMPT b.sql - starting
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as START_TIMESTAMP from dual;
-- found reference to file ./b.sql
-- starting with processing, pwd=/tmp/test
celkem 52
drwxr-xr-x 2 user user 4096 zář 14 21:47 .
drwxrwxrwt 38 root root 36864 zář 14 21:48 ..
-rw-r--r-- 1 user user 51 zář 14 21:45 a.sql
-rw-r--r-- 1 user user 51 zář 14 21:45 b.sql
-rw-r--r-- 1 user user 590 zář 14 21:46 start.sql
-- WARNING: Could not find the referenced file ./b.sql
WARNING: Could not find the referenced file ./b.sql
PROMPT b.sql - finished
select to_char(current_timestamp,'YYYY-MM-DD HH24:MI:SS.FF3') as FINISH_TIMESTAMP from dual;
--
--
spool off
user#mycomputer:/tmp/test$
The script has read access to files, everything is in the same folder, where I start the script, It also does not matter, if I reference the file with the current directory prefix "./" or not, it is just never found. Interesting is, the check during the start of the script passes correctly, the only problem is the searching within the function... I have also tried to call the function with preceded "." and without it, it makes no difference... There are also no trailing spaces in the referenced file names... It also makes no difference if I declare the variables inside of the function as local or not.
So I have no idea, what could be the problem, maybe I just looked too long on it and cannot see something - seems it has to be something trivial, like the function is started in some different current directory - (but pwd and ls shows the directory is always correct...???) - Any help or pointers will be appreciated.

Thank you for the comments, it brought me to the solution. I found out, this problem was related to the fact, the test sql files were created on Windows, so they had in the end of line always the <CR><LF> characters. This leads to the fact in the original bash script I posted, the script line
while read script_line
puts into the variable not only the given file name from the line, preceded by the characters ##
##a.sql
but also the <CR> character - in the variable is then the value
##a.sql<CR>
what was the cause the file a.sql etc. could never be found. Of course the character is invisible, so therefore it has not been shown on any debug echoing I made here - I had to put the content of $file_name between some another characters and then i could see it in the echoed test... I made also some other corrections and the final working script looks in the following way, if somebody needs to join referenced SQL scripts into one, this does it:
#!/bin/bash
#
#
function err () {
echo $* > "/dev/stderr"
}
function replace_subscripts_placeholders() {
local PROCESSING_FILENAME=$1
echo "-- processing file $PROCESSING_FILENAME"
while read script_line || [ -n "$script_line" ]; do
local script_line_NO_LEAD_SPACE="$(echo -e "${script_line}" | sed -e 's/^[[:space:]]*//' | sed -e 's/^M$//')"
if [[ $script_line_NO_LEAD_SPACE == \#\#* ]] ; then
local file_name="${script_line_NO_LEAD_SPACE#\#\#}"
echo "-- found reference to file $file_name"
if [ -f "$file_name" ]; then
replace_subscripts_placeholders $file_name
else
err_msg="WARNING: Could not find the referenced file $file_name"
echo "-- $err_msg"
err $err_msg
fi
else
echo "$script_line"
fi
done < $PROCESSING_FILENAME
}
if test -z "$1" ; then
err "Usage: oracle-script-compressor.sh {file_name_to_process} [> output_file]"
err " Be aware, if the referenced files within {file_name_to_process} are not specified by absolute paths, you have to start the script from corresponding directory."
err " If the part [> output_file] is omitted, then this script writes to standard output"
err " If the part [>> output_file] is used, then the result will be appended to output_file it it exists before the processing"
else
if [ -f "$1" ]; then
replace_subscripts_placeholders $1
else
echo "file $1 does not exist"
fi
fi

Related

For loop in script with arrays and script/terminal inconsistency

I want to loop through a list of directories (a subset of the directories in a folder) and do operations with them. However, for some reason it is not working. Here is the code where I am just echoing all of them:
#!/bin/bash
cd images
array=$(ls -d *)
selection=(${array[#]:1:12})
cd ..
for sub in ${selection[#]}
do
echo $sub
mkdir $HOME/Projects/PhD/0_project/fMRI/scans/temp/temp_$sub
done
The ouptut I get for the echo command is:
04
306
307
3
And the folders: temp_3, temp_04, temp_306, temp_307
HOWEVER, if I run each single line in bash in the termiinal (interactive mode, no script) I do get the correct output for the echo command:
306
307
308
309
310
311
312
314
317
318
323
324
And for the mkdir command: temp_306, temp_307... temp_324
Any idea about this strange and inconsistent behaviour? What am I missing?
Thanks for your help.
result of $(ls -d *) is a string.
you are slicing a string not an array.
remove :1:12.
#!/bin/bash
cd images || exit
array=(*/) # result is array of subdirectories with / on end of each
selection=("${array[#]%/}") # remove trailing slashes from that array.
selection_sliced=("${selection[#]:1:12}") # if you want to slice.
cd .. || exit
for sub in "${selection_sliced[#]}"
do
echo "$sub"
mkdir "$HOME/Projects/PhD/0_project/fMRI/scans/temp/temp_$sub"
done

bash to verify file type integrity and create log

I am trying to use bash to verify the integrity of specific downloads .bam files. There are two parts (bash 1) runs the command to verify the .bam files which creates .txt files, and creates a process.log. That part works perfect, what I am getting an error in is checking each of the .txt files for a string (SUCCESS) and if it is found then in the process.log that file is verified if it is not found then that file is corrupt. Currently the terminal displays the status and then gives an error. Thank you :).
bash part 1
logfile=/home/cmccabe/Desktop/NGS/API/5-4-2016/process.log
for f in /home/cmccabe/Desktop/NGS/API/5-4-2016/*.bam ; do
echo "Start bam validation creation: $(date) - File: $f"
bname=`basename $f`
pref=${bname%%.bam}
bam validate --in $f --verbose 2> /home/cmccabe/Desktop/NGS/API/5-4-2016/bam_validation/${pref}_validation.txt
echo "End bam validation creation: $(date) - File: $f"
done >> "$logfile"
echo "Start verifying $(date) - File: $file"
value=$( grep -ic "(SUCCESS)" /home/cmccabe/Desktop/NGS/API/5-4-2016/bam_validation/*.txt )
bash part 2
if [ $value -eq 1 ]
then
echo "bam file is verified and complete"
else
echo "bam is corrupt, check log for reason"
echo "End bam verify: $(date) - File: $f"
fi
done >> "$logfile"
erorr
Start verifying Thu May 5 12:49:10 CDT 2016 - File:
/home/cmccabe/Desktop/loop.sh: line 11: [: too many arguments
bam is corrupt, check log for reason
End bam verify: Thu May 5 12:49:10 CDT 2016 - File: /home/cmccabe/Desktop/NGS/API/5-4-2016/NA19240.bam
/home/cmccabe/Desktop/loop.sh: line 18: syntax error near unexpected token `done'
/home/cmccabe/Desktop/loop.sh: line 18: `done >> "$logfile"'
file that is created to check for SUCCESS
Number of records read = 24723078
Number of valid records = 24723078
TotalReads(e6) 24.72
MappedReads(e6) 24.57
PairedReads(e6) 0.00
ProperPair(e6) 0.00
DuplicateReads(e6) 7.33
QCFailureReads(e6) 0.00
MappingRate(%) 99.38
PairedReads(%) 0.00
ProperPair(%) 0.00
DupRate(%) 29.66
QCFailRate(%) 0.00
TotalBases(e6) 4332.46
BasesInMappedReads(e6) 4325.68
Returning: 0 (SUCCESS)
The error is due to simplest of the reasons, in bash if you use a single parentheses [] to evaluate a condition, it is nothing but an implicit way of using the bash test expression which expands the $value as a string containing spaces, special characters as separate parameters. Here you have left the variable ambiguous which could be expanded to multiple parameters for some error cases.
All you need to do is enclose that variable within double quotes, so that it is treated as one single string.
if [ "$value" == 1 ]; then

Add input to an existing file with Bash

I am working on a library record app (for school).
I need to be able to collect user input and write to an existent file (add new record). However, when I try to do so, I get the following error:
./minilib.sh: line 12: : No such file or directory
Here is my function for adding new records
records = "/lib_records.txt"
add_book(){
echo
echo "Enter Book Name:"
read name
echo "Enter Book Author:"
read author_name
echo "$name $author_name" >> "$records" #this is my line 12
}
Any idea what may be causing the error? Any help is greatly appreciated.
Here are the file permissions:
-rwxrwxrwx. 1 GSUAD\ GSUAD\domain^users 0 Oct 30 18:04 lib_records.txt
-rwxrwxrwx. 1 GSUAD\ GSUAD\domain^users 1253 Oct 30 18:40 minilib.sh
Here are 2 issues for your shell script:
records="./lib_records.txt": should not have space before and after =
"./lib_records.txt" instead of "/lib_records.txt"
Here is modified script for you.
records="./lib_records.txt"
add_book(){
echo
echo "Enter Book Name:"
read name
echo "Enter Book Author:"
read author_name
echo "$name $author_name" >> "$records" #this is my line 12
}
add_book
You shouldn't store file in / directory (/lib_records.txt), because you will probably get a Permission denied error. Secondly, remove spaces in the first line.

How to batch files in shell script?

I would like to build a shell script to automatically archive object files into a static library, and copy all the headers and .a file to desired directory.
However, as the number of obj files grows, the following mess gets worse:
8 CWD=$(pwd)
9
10 FILE1="SDL_Logger.o"
11 HEADER1="SDL_Logger.h"
12 FILE2="SDL_Initializer.o"
13 HEADER2="SDL_Logger.h"
14 ARC="libsdlhelper.a"
15
16 INCLUDE=~/include
17 LIB=~/lib
18
19 if [ ! -f $FILE1 ];
20 then
21 echo " error: file $FILE1 does not exist. Abort."
22 else
23 if [ ! -f $FILE2 ];
24 then
25 echo " error: file $FILE2 does not exist. Abort."
26 else
27 echo " building archive... "
28 ar rs $ARC $FILE1 $FILE2
29
31 # lib
32 cp $ARC $LIB
34
35 # include
36 cp $HEADER1 $INCLUDE
37 cp $HEADER2 $INCLUDE
39
41 fi
42 fi
So, if I were to group all files into ONE variable like:
FILE="obj1.o obj2.o ... "
How would I check the existence of each file, and copy them(headers)? I can only do this one by one, which will be soon unacceptable.
Here is what I came up with to help you.
The best way to do this is using an array for FILE
short example to implement what you are trying to do:
# !/bin/bash
#define an array of files to look for
FILES=("SDL_Logger.h" "SDL_Initializer.h" "libsdlhelpere.o")
#define your directories
INCLUDE=~/include
LIB=~/lib
#set the amount of entries in our array
SIZE=${#FILES[#]}
# use for loop read all FILES
for (( i=0; i<${SIZE}; i++ ));
do
echo "checking for file: "${FILES[$i]}
if [ ! -f ${FILES[$i]} ];
then
echo " error: file"${FILES[$i]}" does not exist. Abort."
else
echo " building archive... "
fi
done
Here we define FILES as our array
FILES=("SDL_Logger.h" "SDL_Initializer.h" "libsdlhelpere.a")
then we Find the size of files
SIZE=${#FILES[#]}
Then we go through each file and execute a command which is echo and then our if statement:
for (( i=0; i<${SIZE}; i++ ));
do
Here is how we call our item in the array...
${FILES[$i]}
so in what we are doing in plain english is:
echo FILES[1]....
IF FILES[1] doesn't exist then error....
else execute building archive...
echo FILES[2]....
IF FILES[2] doesn't exist then error...
etc...
This will repeat until the SIZE of FILES in met.
Using this method you can also just define the beginning part of the file name such as
"SDL_Initializer" and have your loop add in the .o .h. .a etc... Maybe another array FILETYPES ;)
hope this helped...
Sorry I didn't complete the code for you :)
Here is an answer to the question as asked:
#!/bin/bash
FILES="SDL_Logger.o SDL_Initializer.o"
HEADERS="${FILES//.o/.h}"
ARC="libsdlhelper.a"
INCLUDE=~/include
LIB=~/lib
for f in ${FILES}; do
if [ ! -f "${f}" ]; then
echo "error: file ${f} does not exist. Abort."
exit 1
fi
done
ar rs ${ARC} ${FILES}
cp ${ARC} ${LIB}
cp ${HEADERS} ${INCLUDE}
However, as the comments point out, the right tool for this job is make. For example GNU make. If you plan on developing software on a unix platform, investing time learning make is time well spent.

No response from [ -f /path/to/file ]

I am trying to get my Capistrano deploy script working, but it is not doing the symlinking as it is configured to do as shown below.
set :linked_files, %w{config/database.yml}
set :linked_dirs, %w{log tmp vendor/bundle public/system}
When it runs the related command, I get the following:
WARN [SKIPPING] No Matching Host for /usr/bin/env [ -f /path/to/shared/config/database.yml ]
If I run this command on the server, either through ssh or through logging onto the server and running the command, I get no response from the command.
user: ~
$ [ -f /path/to/shared/config/database.yml ]
user: ~
$
The file does exist in the specified location and has permissions.
user: ~
$ ll /path/to/shared/config/
total 4.0K
drwxrwxr-x 2 user group 33 Nov 30 10:58 .
drwxrwxr-x 7 user group 89 Nov 30 10:58 ..
-rwxrwxr-x 1 user group 805 Nov 30 10:58 database.yml
user: ~
Shouldn't this return a true or a false, instead of nothing? Is there a configuration I may have changed that suppresses the output? I get no response at all whether the file exists or not.
In your response to the actual question you ask, test (which is what [ is an alias for) does in fact not return output to stdout. It returns an exit code.
user: ~
$ [ -f /path/to/shared/config/database.yml ] # if the file exists
user: ~
$ echo $?
0
user: ~
$ [ -f /path/to/shared/config/database.yml ] # if the file does not exist
user: ~
$ echo $?
1
test -f /path/to/file (or [ -f /path/to/file ]) yields an exit code of 0 if the file exists or 1 if it does not. If you want to check that a file is there and echo the path to it, try:
[ -f /path/to/file ] && echo "/path/to/file"

Resources