Shell Script error: command not found - bash

Here is my code aiming to diff .in and .out file in batches. Both .in and .out files are in same directory and sorted by name. So, two files needed to test should be adjacent in directory. But when I want to use outFile=${arr[$(i++)]} to get the .out file, it shows i++: command not found. What's the error in my script?
#!/bin/sh
dir=$PATH
arr=($dir/*)
for((i=0;i<${#arr[#]};i++));do
inFile=${arr[$i]}
outFile=${arr[$(i++)]}
if diff $inFile $outFile > /dev/null; then
echo Same
else
echo $inFile
fi
done

Use $(( i++ )). That's two (not one) parenthesis.
$( ) runs shell commands.
$(( )) evaluates arithmetic expressions.
Also:
Your script uses bash features (arrays), it's best to use #!/bin/bash to avoid confusion.
I'm not sure what you expect dir=$PATH to do? $PATH is a special environment variable that is used for the lookup of commands. This is probably not what you want, if I understand the intent of the script correctly.
i++ will increment the value after being used; so here inFile and outFile are in fact the same! You probably want to use ++i (which will modify the variable and then use it), or just i + 1 (which will not modify the variable).

The stuff in brackets is already evaluated in arithmetic context, like within $(( ... )). So you can do:
for (( i=0; i < ${#arr[#]}; ));do
inFile=${arr[i++]}
outFile=${arr[i++]}
References:
https://www.gnu.org/software/bash/manual/bashref.html#Arrays
The subscript is treated as an arithmetic expression ...
https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic
Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax.

Related

How to delete folders that fail a condition in bash script

I have a number of folders that are constantly and automatically generated. Some are garbage and need to be cleared out. Each folder produces a generations.txt which I want to count the important lines to determine whether or not the folder should be deleted. I'd like to have a bash script I can run every so often to clean things up.
Here's what I have. I can echo the command I want but I don't believe it outputs the integer to compare to 5. Any suggestions would really help me out. Please and thank you!
#!/bin/bash
SEARCHABLES="grep -Evc 'Value:' "
for d in */
do
PATH=$d'generations.txt'
COMMAND=$SEARCHABLES$PATH
if $COMMAND < 5
then
rm -rf $d
fi
done
You're not getting the output of the command, you need $(...) to execute a command and substitute its output.
To perform the arithmetic comparison, you have to put it inside ((...)).
#!/bin/bash
SEARCHABLES="grep -Evc 'Value:' "
for d in */
do
PATH="$d"'generations.txt'
COMMAND=$SEARCHABLES$PATH
if (( $($COMMAND) < 5 ))
then
rm -rf "$d"
fi
done
See BashFAQ/050 - I'm trying to put a command in a variable, but the complex cases always fail!
for a more detailed explanation.
In short, embedding a command in a variable is a faulty approach to the problem here because the single quotes in 'Value:' will be treated like literal data to search for. Syntax parsing happens before expansions, so you can't embed quotes in a variable like that. What you need is a function:
_count() {
grep -Evc 'Value:' "$1"
}
_count "$PATH"
Then compare the output of the function using an arithmetic expression:
occurrences=$( _count "$PATH" )
if (( occurrences < 5 )) ; then
...
fi

Getting Bash to parse variables from file input [duplicate]

This question already has answers here:
Forcing bash to expand variables in a string loaded from a file
(13 answers)
Closed 7 years ago.
Let's say I have a file called path.txt containing the text $HOME/filedump/ on a single line. How can I then read the contents of path.txt into a variable, while having Bash parse said content?
Here's an example of what I'm trying to do:
#!/bin/bash
targetfile="path.txt"
target=$( [[ -f $targetfile ]] && echo $( < $targetfile ) || echo "Not set" )
echo $target
Desired output: /home/joe/filedump/
Actual output: $HOME/filedump/
I've tried using cat in place of <, wrapping it in quotes, and more. Nothing seems to get me anywhere.
I'm sure I'm missing something obvious, and there's probably a simple builtin command. All I can find on Google is pages about reading variables from ini/config files or splitting one string into multiple variables.
If you want to evaluate the contents of path.txt and assign that to target, then use:
target=$(eval echo $(<path.txt))
for example:
$ target=$(eval echo $(<path.txt)); echo "$target"
/home/david/filedump/
This might not necessarily suit your needs (depending on the context of the code you provided), but the following worked for me:
targetfile="path.txt"
target=$(cat $targetfile)
echo $target
Here's a safer alternative than eval. In general, you should not be using configuration files that require bash to evaluate their contents; that just opens a security risk in your script. Instead, detect if there is something that requires evaluation, and handle it explicitly. For example,
IFS= read -r path < path.txt
if [[ $path =~ '$HOME' ]]; then
target=$HOME/${path#\$HOME}
# more generally, target=${path/\$HOME/$HOME}, but
# when does $HOME ever appear in the *middle* of a path?
else
target=$path
fi
This requires you to know ahead of time what variables might appear in path.txt, but that's a good thing. You should not be evaluating unknown code.
Note that you can use any placeholder instead of a variable in this case; %h/filedump can be detected and processed just as easily as $HOME/filedump, without the presumption that the contents can or should be evaluated as shell code.

Double parentheses to increment a variable in bash

Suppose I increment a variable in bash. For instance,
> i=0; for f in `ls *.JPG`; do echo $f $i; ((i++)); done
a0.jpg 0
a1.jpg 1
...
Now I wonder why I need those double parentheses to increment i.
The double parentheses construct is a shell feature to support arithmetic operations.
The same construct can also be used for Loops and special numerical constants.
Also, copied from the first link :
# -----------------
# Easter Egg alert!
# -----------------
# Chet Ramey seems to have snuck a bunch of undocumented C-style
#+ constructs into Bash (actually adapted from ksh, pretty much).
# In the Bash docs, Ramey calls (( ... )) shell arithmetic,
#+ but it goes far beyond that.
# Sorry, Chet, the secret is out.
i++ is a perfectly valid file name, and if I have access to your system, I can make that into a command that does something you don't want.
Try creating a file, /bin/i++ with this content:
#!/bin/sh
echo 'Gotcha'
and then chmod +x /bin/i++

print a variable using another

I have my script setup like this, i want to print switch names using loop.
swn1="something"
swn2="somethingelse"
for (( i=1; i<="$ii"; i++ ))
do
echo "$swn$i "
done
I have searched and searched but no luck, can anyone help me how to print swn using for loop ?
Sounds like you want an array...
swn=("something" "something else")
for i in "${swn[#]}"
do
echo $i
done
The solution for that is variable indirection.
swn1="something"
swn2="somethingelse"
for (( i=1; i<="$ii"; i++ ))
do
var="swn$i"
echo "${!var}"
done
Although normally you could solve your problem with arrays, but the way to print a variable using another is through it.
As explained in the bash manual:
If the first character of parameter is an exclamation point (!), a
level of variable indirection is introduced. Bash uses the value of
the variable formed from the rest of parameter as the name of the
variable; this variable is then expanded and that value is used in the
rest of the substitution, rather than the value of parameter itself.
This is known as indirect expansion. The exceptions to this are the
expansions of ${!prefix} and ${!name[#]}. The
exclamation point must immediately follow the left brace in order to
introduce indirection.
You can't do this. swn1 is one token, swn2 is another. It looks like you want an array, and if you want an array in a bash script you either want to process the values as separate lines in a file, or switch to using Perl.
Separate lines:
echo "something" > swnfile;
echo "somethingelse" >> swnfile;
cat file | while read line; do
echo "$line";
done

How run AWK command in Shell Script?

I am trying to create files using AWK with in a for loop but getting empty file. It looks like AWK is not getting executed. Can you please advise, what is incorrect here?
I have a file $LOG/order_"$id".txt which has some lines between 2 strings CUT:1 and CUT2 etc.. I just need these lines in separate files so I wrote this for loop.
for (( i=1; i<=$CNTLN; i++ ))
do
j=`expr $i - 1`
/usr/bin/awk '/CUT:$j/, /CUT:$i/' $LOG/order_"$id".txt > $LOG/order_"$id"_"$i".txt
done
This generate blank file however if I copy and past this command on shell, it works.
Please advise.
Your quoting is correct, however you should use variable passing instead of trying to embed shell variables within the AWK script.
for (( i=1; i<=CNTLN; i++ ))
do
(( j = i - 1 ))
/usr/bin/awk -v i="$i" -v j="$j" '$0 ~ "CUT:" j, $0 ~ "CUT:" i' "$LOG/order_$id.txt" > "$LOG/order_${id}_$i.txt"
done
When using AWK variables, you have to use the tilde match operator instead of the slash-delimited form.
You don't say which shell you're using, but based on the for statement, I'd guess Bash, Korn or Z shell. If it's one of those, then you can do integer arithmetic as I've shown without using the external expr utility. In the case of ksh or zsh, you can do float math, too.
You could eliminate the line that calculates j and include it in the for statement.
for (( i=1, j=0, i<=CNTLN; i++, j++ ))
In a couple of places you use all-caps variable names. I recommend making a habit of not doing this in order to avoid name collisions with shell or environment variables. It's not an issue in this specific case, but it's a good habit to be in.
Actually, now that I think about it. The shell loop could be eliminated and the whole thing written in AWK.

Resources