Can anyone help me understand this bash script? - bash

I am trying to do some analysis of SSD firmware and have found a bash script called firmware.sh that seems interesting to me. However, I really don't know what I am looking at here.
If anyone can help me understand what this code might be used for, or what it's doing, I would greatly appreciate it!
Here's the bash:
#!/bin/sh -e
FIRMWARE_DIRS="/lib/firmware /usr/local/lib/firmware"
err() {
echo "$#" >&2
if [ -x /usr/bin/logger ]; then
/usr/bin/logger -t "${0##*/}[$$]" "$#"
fi
}
if [ ! -e /sys$DEVPATH/loading ]; then
err "udev firmware loader misses sysfs directory"
exit 1
fi
for DIR in $FIRMWARE_DIRS; do
[ -e "$DIR/$FIRMWARE" ] || continue
echo 1 > /sys$DEVPATH/loading
cat "$DIR/$FIRMWARE" > /sys$DEVPATH/data
echo 0 > /sys$DEVPATH/loading
exit 0
done
echo -1 > /sys$DEVPATH/loading
err "Cannot find firmware file '$FIRMWARE'"
exit 1
Of particular interest to me is the for loop... I think I understand that the $NAME syntax is used for variables in bash but I don't know what those variables are referencing. Thank you for your consideration!

I'll try to explain this line by line.
FIRMWARE_DIRS="/lib/firmware /usr/local/lib/firmware"
FIRMWARE_DIRS is set up with two directories separated by a space. This is set up for the for loop later on in the script.
...
for DIR in $FIRMWARE_DIRS; do
For each loop, DIR is set to each directory stored in FIRMWARE_DIRS
[ -e "$DIR/$FIRMWARE" ] || continue
[ denotes the start of a test, much like if, and ] marks the end of this test.
-e checks if the argument passed is a file or directory that exists.
|| means or and anything to the right of this will execute if the test to the left fails.
continue stops the current iteration of a loop which starts on the next iteration.
FIRMWARE is presumably an environment variable set up prior to this
script running. You can see its value if it has been set up on login
by executing the command echo $FIRMWARE on the command line.
echo 1 > /sys$DEVPATH/loading
Truncates the file /sys$DEVPATH/loading if it exists, then outputs the number 1 to this file.
cat "$DIR/$FIRMWARE" > /sys$DEVPATH/data
Truncates the file /sys$DEVPATH/data if it exists, then outputs the contents of the file(s) $DIR/$FIRMWARE to /sys$DEVPATH/data. If FIRMWARE contains a wildcard *, it will copy the contents of all the files matched.
echo 0 > /sys$DEVPATH/loading
Truncates the file sys$DEVPATH/loading if it exists, then outputs the number 0 to this file.
exit 0
Exits the script with the return status 0 (means it completed OK). This has the effect of ending the script in the for loop at this point for any iteration which passed the test above (the one checking the file or directory exists).
Overall, it looks like it's checking for the first directory that exists in FIRMWARE_DIRS, copies one or more firmware files from there to another location
(/sys$DEVPATH/data) and exits as soon as it's done that once.

Related

Bash Check if a Script Ran Successfully (Exit Code not Working)

I have the following bash script:
echo one
echo two
cd x
echo three
which fails at the 3rd line as there is no directory named x. However, after running the script, when I do $?, 0 is returned, even though the script has an error. How do I detect whether the script ran successfully or not?
Check the condition of directory existence in the script statements:
[ -d x ] && cd x || { echo "no such directory"; exit 1; }
Or put set -e after shebang line:
#!/bin/bash
set -e
echo one
echo two
cd x
echo three
You should end with an exit statement
echo one
echo two
cd x
exitCode=$?
echo three
exit $exitCode;
Then
./myscript
echo $?
1
I have searched all over with no clear answer to this. Put simply it doesn't appear to be a native feature in bash. So I will give you the hard way.
To make a .sh script with multiple commands and you don't know if any will error but you want to check if at least one has. You would need to put a $? at the end of literally every command and redirect it to a text file. Once it's in the text file you could format it like.
Command1 = 0 Success.
Command2 = 127 Fail.
Or you could just add the numbers to the file run it through some kind of calculator to add everything together and if the output is greater than zero then the command at some point failed. But this won't be overly useful if you want the exact number and there are more than one failure.
UPDATED - This is the best way I could find.
You can put this at the top of your script file to catch any errors and exit if it fails.
set -euo pipefail
Feel free to read the manual pages.

Single command line parameter not running if statement

I'm writing a bash script to organise a .txt file, of which is loaded with the bash script using the command line parameter:
bash ./myScript.sh textfile.txt
I have an if statement (below, non-functional) that's supposed to detect if the .txt file exists in the same directory. If it does, it confirms it with the user and the script continues. If it doesn't exist, the script is supposed to continually check if the user's input is an existing file in the working directory before continuing.
Here's what I have so far:
#CS101 Assignment BASH script
CARFILE=$1
wc $CARFILE
if [ -f $CARFILE ]
then
echo "$CARFILE exists, please continue"
else
echo "This file does not exist, please enter the new filename and press [ENTER]"
read CARFILE
echo "We have detected that you're using $CARFILE as your cars file, please continue."
fi
It simply outputs: exists, please continue if you don't run it with a .txt (ie bash jag32.sh instead of bash jag32.sh textfile.txt).
Can anyone help out please?
Thanks.
There are two problems here. First, you're calling wc $CARFILE before you check if $CARFILE exists. If you specify no paramaters (bash jag32.sh), then this becomes simply wc, which will wait forever for input on stdin.
Before going any further, bash -x is your friend: this will trace the execution of your script, showing you exactly what commands the script is running. This will often help illuminate problems:
bash -x jag32.sh
The problem with your test:
if [ -f $CARFILE ]
Is that if $CARFILE is empty, it becomes simply:
if [ -f ]
Which evaluates to true. What!? That's because the file-existence test takes two parameters (-f FILE), and when $CARFILE is empty, you only have one, so it's evaluating as a simple is-this-string-empty? test.
And this is why you always quote variables:
if [ -f "$CARFILE" ]
If $CARFILE is empty, this becomes:
if [ -f "" ]
Which will evaluate to false.
In addition to checking for the existence of the file, you could also first check if $1 has any value, or if your script has been passed any arguments. The bash(1) and test(1) man pages have details that should point you in the right direction.

shell command to skip file in sequence

I run a C++ code for several data files in sequence using:
for i in $(seq 0 100); do ./f.out c2.$(($i*40+495)).bin `c2.avg.$(($i*40+495)).txt; done`
Now if some input files are missing, say c2.575.bin is missing then the command is not executed for the rest of the files. How could I modify the shell command to skip the input files those are missing and move to the next input file?
Thanks.
in the loop, test if file exists before calling a program operating on that file:
for i in $(seq 0 100); do
INPUT=c2.$(($i*40+495)).bin
test -e $INPUT && ./f.out $INPUT c2.avg.$(($i*40+495)).txt
done
This way the ./f.out ... will be executed only for existing input files.
See man test for details.
BTW, the && notation is a shorthand for if. See help if or man sh.
You can use {0..100} instead of $(seq 0 100) for better readability. You can put the following code in a script and execute the script. e.g., runCode.bash
#!/bin/bash
for i in {0..100}
do
# Assign a variable for the filenames
ifn=c2.$(($i*40+495)).bin
# -s option checks if the file exists and size greater than zero
if [ -s "${ifn}" ]; then
./f.out "${ifn}" c2.avg.$(($i*40+495)).txt
else
echo "${ifn} No such file"
fi
done
Change permission and execute the script.
chmod u+x runCode.bash`
./runCode.bash`

simple script errors at 1) in case statement in a file listing script

Couldn't really find a thread similar to this one so I thought I'd make a new thread. I'm trying to make a basic bash script that displays the name of files inside the current directory one at a time.
An echo statement says 'Would you like to preview this file? Y/N', If Yes then the code goes on to display the first 3 lines of that file using the head syntax, the program then exits.
So this is what I had in mind so far,
#!/bin/bash
for file in ./* #This points to current directory with ./* and views files inside.
do
if [[ -f $file ]] #If File Exists, read it
read file
then
echo -e 'Would you like to view this file? Press 1 For Yes and 2 For No'
read boolean #reads the prompt 1 or 2 to then proceed with the case statement below
case $boolean
1)
echo -e 'Preview of File...'
read boolean
2)
echo -e 'Exit Program...'
read boolean
if [ boolean -eq 1 ] #This part of the code finds out if the prompt was answered with 1 (yes), if it was then it produces the first 3 lines of the file, if it was 2 then it quits the program.
then line=$(head -n 3 file)
else
*( echo -e 'Please enter a valid option' #If someone enters 3-9 then the program will require them to enter a valid number.
esac
done
fi
The program displays an error at line 23 "1)" just after initiating a case statement. Is this not the proper syntax? I'm not quite sure why it would do that.
First, you missed in
case "$boolean" in
1)
Second, you missed ;;
read boolean ;;
Also, quote your variables. It seems like there are other errors as well.

shell script working fine on one server but not on another

the following script is working fine on one server but on the other it gives an error
#!/bin/bash
processLine(){
line="$#" # get the complete first line which is the complete script path
name_of_file=$(basename "$line" ".php") # seperate from the path the name of file excluding extension
ps aux | grep -v grep | grep -q "$line" || ( nohup php -f "$line" > /var/log/iphorex/$name_of_file.log & )
}
FILE=""
if [ "$1" == "" ]; then
FILE="/var/www/iphorex/live/infi_script.txt"
else
FILE="$1"
# make sure file exist and readable
if [ ! -f $FILE ]; then
echo "$FILE : does not exists. Script will terminate now."
exit 1
elif [ ! -r $FILE ]; then
echo "$FILE: can not be read. Script will terminate now."
exit 2
fi
fi
# read $FILE using the file descriptors
# $ifs is a shell variable. Varies from version to version. known as internal file seperator.
# Set loop separator to end of line
BACKUPIFS=$IFS
#use a temp. variable such that $ifs can be restored later.
IFS=$(echo -en "\n")
exec 3<&0
exec 0<"$FILE"
while read -r line
do
# use $line variable to process line in processLine() function
processLine $line
done
exec 0<&3
# restore $IFS which was used to determine what the field separators are
IFS=$BAKCUPIFS
exit 0
i am just trying to read a file containing path of various scripts and then checking whether those scripts are already running and if not running them. The file /var/www/iphorex/live/infi_script.txt is definitely present. I get the following error on my amazon server-
[: 24: unexpected operator
infinity.sh: 32: cannot open : No such file
Thanks for your helps in advance.
You should just initialize file with
FILE=${1:-/var/www/iphorex/live/infi_script.txt}
and then skip the existence check. If the file
does not exist or is not readable, the exec 0< will
fail with a reasonable error message (there's no point
in you trying to guess what the error message will be,
just let the shell report the error.)
I think the problem is that the shell on the failing server
does not like "==" in the equality test. (Many implementations
of test only accept one '=', but I thought even older bash
had a builtin that accepted two '==' so I might be way off base.)
I would simply eliminate your lines from FILE="" down to
the end of the existence check and replace them with the
assignment above, letting the shell's standard default
mechanism work for you.
Note that if you do eliminate the existence check, you'll want
to either add
set -e
near the top of the script, or add a check on the exec:
exec 0<"$FILE" || exit 1
so that the script does not continue if the file is not usable.
For bash (and ksh and others), you want [[ "$x" == "$y" ]] with double brackets. That uses the built-in expression handling. A single bracket calls out to the test executable which is probably barfing on the ==.
Also, you can use [[ -z "$x" ]] to test for zero-length strings, instead of comparing to the empty string. See "CONDITIONAL EXPRESSIONS" in your bash manual.

Resources