shell script: if statement - bash

I'm following the tutorial here: http://bash.cyberciti.biz/guide/If..else..fi#Number_Testing_Script
My script looks like:
lines=`wc -l $var/customize/script.php`
if test $lines -le 10
then
echo "script has less than 10 lines"
else
echo "script has more than 10 lines"
fi
but my output looks like:
./boot.sh: line 33: test: too many arguments
script has more than 10 lines
Why does it say I have too many arguments? I fail to see how my script is different from the one in the tutorial.

wc -l file command will print two words. Try this:
lines=`wc -l file | awk '{print $1}'`
To debug a bash script (boot.sh), you can:
$ bash -x ./boot.sh
It'll print every line executed.

wc -l file
outputs
1234 file
use
lines=`wc -l < file`
to get just the number of lines. Also, some people prefer this notation instead of backticks:
lines=$(wc -l < file)
Also, since we don't know if $var contains spaces, and if the file exists:
fn="$var/customize/script.php"
if test ! -f "$fn"
then
echo file not found: $fn
elif test $(wc -l < "$fn") -le 10
then
echo less than 11 lines
else
echo more than 10 lines
fi

Also, you should use
if [[ $lines -gt 10 ]]; then
something
else
something
fi
test condition is really outdated, and so is it's immediate successor, [ condition ], mainly because you have to be really careful with those forms. For example, you must quote any $var you pass to test or [ ], and there are other details that get hairy. (tests are treated in every way as any other command). Check out this article for some details.

Related

Reading in File line by line w/ Bash

I'm creating a bash script to read a file in line by line, that is formatted later to be organised by name and then date. I cannot see why this code isn't working at this time though no errors show up even though I have tried with the input and output filename variables on their own, with a directory finder and export command.
export inputfilename="employees.txt"
export outputfilename="branch.txt"
directoryinput=$(find -name $inputfilename)
directoryoutput=$(find -name $outputfilename)
n=1
if [[ -f "$directoryinput" ]]; then
while read line; do
echo "$line"
n=$((n+1))
done < "$directoryoutput"
else
echo "Input file does not exist. Please create a employees.txt file"
fi
All help is very much appreciated, thank you!
NOTE: As people noticed, I forgot to add in the $ sign on the data transfer to file, but it was just in copying my code, I do have the $ sign in my actual application and still no result
Reading in File line by line w/ Bash
The best and idiomatic way to read file line by line is to do:
while IFS= read -r line; do
// parse line
printf "%s" "$line"
done < "file"
More on this topic can be found on bashfaq
However don't read files in bash line by line. You can (ok, almost) always not read a stream line by line in bash. Reading a file line by line in bash is extremely slow and shouldn't be done. For simple cases all the unix tools with the help of xargs or parallel can be used, for more complicated awk and datamesh are used.
done < "directoryoutput"
The code is not working, because you are passing to your while read loop as input to standard input the content of a file named directoryoutput. As such a file does not exists, your script fails.
directoryoutput=$(find -name $outputfilename)
One can simply append the variable value with newline appended to a read while loop using a HERE-string construction:
done <<< "$directoryoutput"
directoryinput=$(find -name $inputfilename)
if [[ -f "$directoryinput" ]]
This is ok as long as you have only one file named $inputfilename in your directory. Also it makes no sense to find a file and then check for it's existance. In case of more files, find return a newline separated list of names. However a small check if [ "$(printf "$directoryinput" | wc -l)" -eq 1 ] or using find -name $inputfilename | head -n1 I think would be better.
while read line;
do
echo "$line"
n=$((n+1))
done < "directoryoutput"
The intention is pretty clear here. This is just:
n=$(<directoryoutput wc -l)
cat "directoryoutput"
Except that while read line removed trailing and leading newlines and is IFS dependent.
Also always remember to quote your variables unless you have a reason not to.
Have a look at shellcheck which can find most common mistakes in scripts.
I would do it more like this:
inputfilename="employees.txt"
outputfilename="branch.txt"
directoryinput=$(find . -name "$inputfilename")
directoryinput_cnt=$(printf "%s\n" "$directoryinput" | wc -l)
if [ "$directoryinput_cnt" -eq 0 ]; then
echo "Input file does not exist. Please create a '$inputfilename' file" >&2
exit 1
elif [ "$directoryinput_cnt" -gt 1 ]; then
echo "Multiple file named '$inputfilename' exists in the current path" >&2
exit 1
fi
directoryoutput=$(find . -name "$outputfilename")
directoryoutput_cnt=$(printf "%s\n" "$directoryoutput" | wc -l)
if [ "$directoryoutput_cnt" -eq 0 ]; then
echo "Input file does not exist. Please create a '$outputfilename' file" >&2
exit 1
elif [ "$directoryoutput_cnt" -gt 1 ]; then
echo "Multiple file named '$outputfilename' exists in the current path" >&2
exit 1
fi
cat "$directoryoutput"
n=$(<"$directoryoutput" wc -l)

for loop in UNIX Script is not working

for i in `cat ${DIR}/${INDICATOR_FLIST}`
do
X=`ls $i|wc -l`
echo $X
echo $X>>txt.txt
done
I have a code like this to check if file is present in a directory or not
but this is not working and gives error like this:
not foundtage001/dev/BadFiles/RFM_DW/test_dir/sales_crdt/XYZ.txt
You can see there is no space between not found and file path.
I have a code like this to check if file is present in a directory or not ...
It seems that you're trying to read a list of files from ${DIR}/${INDICATOR_FLIST} and then trying to determine if those actually exist. The main problem is:
You're trying to parse ls in order to figure whether the file exists.
This is what results in the sort of output that you see; mix of stderr and stdout. Use the test operator instead as given below.
The following would give tell you whether the file exists or not:
while read line; do
[ -e "${line}" ] && echo "${line} exists" || echo "${line} does not exist";
done < ${DIR}/${INDICATOR_FLIST}
Your file ${INDICATOR_FLIST} has CRLF line terminators (DOS-style). You need to strip out the CR characters, as Unix convention is for LF-only line terminators.
You can tell this by the way "not found" is printed at the start of the line. The immediately preceding character (the last char of the filename) is a CR, which sends the cursor back to the start of the line.
Find a dos2unix utility, or run tr -d \\015 over it (this deletes all CR's indiscriminately).
maybe the location of your file isn't ok.
take this example
m:~ tr$ echo "1 2 3 4 5" > file.txt
m:~ tr$ cat file.txt
1 2 3 4 5
m:~ tr$ for i in `cat file.txt`;do echo $i ;done
1
2
3
4
5
m:~ tr$
you could write the file before "for" and maybe check if the file exists:
echo "location : ${DIR}/${INDICATOR_FLIST}"
if [ -e ${DIR}/${INDICATOR_FLIST} ];then echo "file exists ";else echo "file was not found";fi
for i in `cat ${DIR}/${INDICATOR_FLIST}`;
do
let "X=`ls $i|wc -l`";
echo $X;
echo $X>>txt.txt;
done
The proper syntax is:
for i in `cat data-file`
do
echo $I
done
which you are following, therefore the problem must be in the specification of $DIR and $INDICATOR_LIST so therefore double check the location of your file.

Command not acting properly in script

The command:
value=${value%?}
will remove the last character from a variable.
Is there any logical reason why it would not work from within a script?
In my script it has no effect whatsoever.
if [[ $line =~ "What I want" ]]
then
if [[ $CURRENT -eq 3 ]]
then
echo "line is " $line
value=`echo "$line" | awk '{print $4}'`
echo "value = "$value
value=${value%?}
echo "value = $value "
break
fi
fi
I cant post the whole script, but this is the piece I refer to. The loop is being entered properly, but the 2 echo $value lines return the same thing.
Edit - this question still stands. The code works fine line bu line in a terminal, but all together in a script it fails.
Echo adds an extra line character to $value in this line:
value=`echo "$line" | awk '{print $4}'`
And afaik that extra char is removed with %?, so it seems it does not change anything at all.
Try echo -n instead, which does not add \n to the string:
value=`echo -n "$line" | awk '{print $4}'`
Since you have provided only the relevant part in the code and not the whole file, I'm going to assume that the first line of your file reads `#!/bin/sh'. This is your problem. What you are trying to do (parameter expansion) is specific to bash, so unless /bin/sh points to bash via a symlink, then you are running the script in a shell which does not understand bash parameter expansion.
To see what /bin/sh really is you can do: ls -l /bin/sh. And to remedy the situation, force the script to run in bash by changing the `shebang' at the top to read `#!/bin/bash'

shell script to count readable files

How can I write a shell script file_readable which:
accepts some number of names as arguments,
checks each name to see if it is a regular file and readable, and
outputs a count of the number of such files.
For example:
$ sh file_readable /etc/fstab /etc/ssh/ssh_host_rsa_key /etc/does-not-exist
1
Of these, only /etc/fstab is likely to both exist and be readable.
So far I have put this together but it does not work correctly - can anybody help me please ?:
#!/bin/sh
for filename in "$#"
do
if test -f "$filename"
then echo | wc -l
else echo $?
fi
done
then echo | wc -l
If file exists and is a regular you print number of lines in empty string plus "\n", which is equal 1 always. Sound not quite usable, isn't it?
All you need is incrementing some counter and printing it in the end.
#!/bin/sh
readable_files=0
for filename in "$#"; do
if test -f "$filename"; then
readable_files=$(( readable_files + 1 ))
fi
done
echo "${readable_files}"

Error using [[ ]] and -eq

Shell script snippet:
tagSearch= $(grep '^\#ctags$' ./"$1" | wc -l)
if [[ $tagSearch -ne "0" ]]
then
...
fi
Results in:
line 2: /bb/bin/1: Permission denied
Context:
I'm trying to detect whether a particular pattern exists in a file so I can take a particular action.
I understand the error I'm getting, the detection is working but the script is trying to evaluate the result '1' and run the program '1' in my path. This isn't what I want. How do I get the behavior I'm looking for?
The problem is
tagSearch= $(grep '^\#ctags$' ./"$1" | wc -l)
----------^
You can't use spaces around the equal sign; what you're actually doing here is to temporarily set tagSearch to the empty string in the environment, then invoking grep '^\#ctags$' ./"$1" | wc -l, then trying to run that as a command since the $() will have inserted the result into the command line.
tagSearch=$(grep '^\#ctags$' ./"$1" | wc -l)
Variable assignments in the bash shell should not have a space after the equals. Actually it should never have whitespace in it at all. See below.
tagSearch=$(grep '^\#ctags$' "./$1" | wc -l)
if [[ $tagSearch -ne 0 ]]
then
...
fi
Not important to your error but also of note, when using the double bracket syntax, you don't need to quote that zero any more than the variable you are comparing it with.
Actually your whole code could be re-factored using grep's quite mode and evaluating the return code to see if you got any matches:
if grep '^\#ctags$' "./$1"
then
...
fi
Actually you can have that simpler, because the return code of grep will be 0 if something is found (1 otherwise), so you don't need wc -l. And you can just write:
if `grep -q pattern file`; then echo "yes"; else echo "no"; fi;

Resources