while IFS= read -r -d $'\0' file ... explanation - bash

I do not understand this line of shell script. Doesn't the while statement need a 'test' or [ ] or [[ ]] expression that will set $? to 1 or 0? How does
while IFS= read -r -d $'\0'; do ...; done
do that? Any help understanding the syntax here is greatly appreciated.

In Bash, varname=value command runs command with the environment variable varname set to value (and all other environment variables inherited normally). So IFS= read -r -d $'\0' runs the command read -r -d $'\0' with the environment variable IFS set to the empty string (meaning no field separators).
Since read returns success (i.e., sets $? to 0) whenever it successfully reads input and doesn't encounter end-of-file, the overall effect is to loop over a set of NUL-separated records (saved in the variable REPLY).
Doesn't the while statement need a 'test' or [ ] or [[ ]] expression that will set $? to 1 or 0?
test and [ ... ] and [[ ... ]] aren't actually expressions, but commands. In Bash, every command returns either success (setting $? to 0) or failure (setting $? to a non-zero value, often 1).
(By the way, as nosid notes in a comment above, -d $'\0' is equivalent to -d ''. Bash variables are internally represented as C-style/NUL-terminated strings, so you can't really include a NUL in a string; e.g., echo $'a\0b' just prints a.)

I do not understand this line of shell script. Doesn't the while statement need a 'test' or [ ] or [[ ]] expression that will set $? to 1 or 0? How does [the statement] do that?
Everyone is answering about the -d and IFS= parameter, but no one answered your question about test and [[..]].
All standard Unix commands will return a zero value on success and a non-zero value on exit. The while and if statements use that value to determine whether or not to continue to loop:
Let's say you do the following:
$ touch foo.txt #Create a "foo.txt" file
$ ls foo.txt
foo.txt
$ echo $?
0
$
Notice that the ls command returned a zero on exit. Now try this:
$ rm foo.txt
$ ls foo.txt
ls: foo.txt: No such file or directory
$ echo $?
1
$
If foo.txt doesn't exist, the ls command returns a 1 on exit.
The if and while commands operate on that.
Go into another terminal window and create a foo.txt file. Then return to the original window and try this:
$ while ls foo.txt > /dev/null
> do
> echo "The file foo.txt exists"
> sleep 2
> done
The file foo.txt exists
The file foo.txt exists
The file foo.txt exists
...
Every two seconds, the while will loop through checking for the existence of foo.txt and print out that statement. Now, go back to the second terminal window and delete foo.txt. If you go back to the first terminal window, you'll see that the while loop ended.
You can do the same with the if:
$ if ls foo.txt > /dev/null
> then
> echo "foo.txt exists"
> else
> echo "Whoops, it's gone!"
> fi
This if statement will print either foo.txt exists or Whoops, it's gone! depending whether or not a file called foo.txt exists in the directory.
Notice that if and while work upon the return exit value of the command. There's no need for a test at all.
So why the test, [...], and [[...]] syntax?
Let's say you want to find out whether $foo is equal to $bar. That's where test comes into play. Try this:
$ foo=42
$ bar=42
$ test "$foo" -eq "$bar"
$ echo $?
0
$ bar=0
$ test "$foo" -eq "$bar"
$ echo $?
1
All the test command does is return a zero on true tests and a non-zero value on false tests. This allows you to use the test command in if and while statements.
$ if test $foo -eq $bar
> then
> echo "foo is equal to bar"
> else
> echo "foo and bar are different"
> fi
foo and bar are different
$
So what's [...]?
$ ls -il /bin/test /bin/[
10958 -rwxr-xr-x 2 root wheel 18576 May 28 22:27 /bin/[
10958 -rwxr-xr-x 2 root wheel 18576 May 28 22:27 /bin/test
That first column is the inode. If two files have the same inode, they are hard linked and refer to the same file. Notice that they're both 18,576 bytes on my system.
The [ is just another way to do the test command itself. Both of these lines are the same:
if test $foo -eq $bar
if [ $foo -eq $bar ]
The [ was created to make shell scripts look a wee bit nicer. However, underneath, it's running a test command which will return either a zero or non-zero value that if and while can use.
By the way, in Kornshell and BASH, both [ and test are builtin to the shell. But, they do reference the same builtin command.
The [[...]] is a special version of the test that first appeared in Kornshell. It allows for the use of pattern matches and it's a bit faster since it doesn't spawn another process to run the test command.

Code explanations :
IFS= set IFS to null (input fiels separator)
read command that reads the current line with the following options :
-r : do not allow backslashes to escape any characters
-d continue until the first character of DELIM is read, rather than newline
$'\0' zero byte (the ASCII NUL character)
See
help read

No. The test command and [[ ...]] expression are merely constructs that supply an exit status (the only thing while cares about) that reflects the truth of the conditional expression it contains.
For example, the exit status of [[ $foo = bar ]] is 0 (success) if $foo has the value bar, and a non-zero exist status (specifically 1) otherwise.
A while loop executes its body as long as the exit status of the command following the while keyword is 0. In this code, read has a 0 exit status as long as it can read something from its input.

Related

meaning of read and -d and $'\0' in bash [duplicate]

I do not understand this line of shell script. Doesn't the while statement need a 'test' or [ ] or [[ ]] expression that will set $? to 1 or 0? How does
while IFS= read -r -d $'\0'; do ...; done
do that? Any help understanding the syntax here is greatly appreciated.
In Bash, varname=value command runs command with the environment variable varname set to value (and all other environment variables inherited normally). So IFS= read -r -d $'\0' runs the command read -r -d $'\0' with the environment variable IFS set to the empty string (meaning no field separators).
Since read returns success (i.e., sets $? to 0) whenever it successfully reads input and doesn't encounter end-of-file, the overall effect is to loop over a set of NUL-separated records (saved in the variable REPLY).
Doesn't the while statement need a 'test' or [ ] or [[ ]] expression that will set $? to 1 or 0?
test and [ ... ] and [[ ... ]] aren't actually expressions, but commands. In Bash, every command returns either success (setting $? to 0) or failure (setting $? to a non-zero value, often 1).
(By the way, as nosid notes in a comment above, -d $'\0' is equivalent to -d ''. Bash variables are internally represented as C-style/NUL-terminated strings, so you can't really include a NUL in a string; e.g., echo $'a\0b' just prints a.)
I do not understand this line of shell script. Doesn't the while statement need a 'test' or [ ] or [[ ]] expression that will set $? to 1 or 0? How does [the statement] do that?
Everyone is answering about the -d and IFS= parameter, but no one answered your question about test and [[..]].
All standard Unix commands will return a zero value on success and a non-zero value on exit. The while and if statements use that value to determine whether or not to continue to loop:
Let's say you do the following:
$ touch foo.txt #Create a "foo.txt" file
$ ls foo.txt
foo.txt
$ echo $?
0
$
Notice that the ls command returned a zero on exit. Now try this:
$ rm foo.txt
$ ls foo.txt
ls: foo.txt: No such file or directory
$ echo $?
1
$
If foo.txt doesn't exist, the ls command returns a 1 on exit.
The if and while commands operate on that.
Go into another terminal window and create a foo.txt file. Then return to the original window and try this:
$ while ls foo.txt > /dev/null
> do
> echo "The file foo.txt exists"
> sleep 2
> done
The file foo.txt exists
The file foo.txt exists
The file foo.txt exists
...
Every two seconds, the while will loop through checking for the existence of foo.txt and print out that statement. Now, go back to the second terminal window and delete foo.txt. If you go back to the first terminal window, you'll see that the while loop ended.
You can do the same with the if:
$ if ls foo.txt > /dev/null
> then
> echo "foo.txt exists"
> else
> echo "Whoops, it's gone!"
> fi
This if statement will print either foo.txt exists or Whoops, it's gone! depending whether or not a file called foo.txt exists in the directory.
Notice that if and while work upon the return exit value of the command. There's no need for a test at all.
So why the test, [...], and [[...]] syntax?
Let's say you want to find out whether $foo is equal to $bar. That's where test comes into play. Try this:
$ foo=42
$ bar=42
$ test "$foo" -eq "$bar"
$ echo $?
0
$ bar=0
$ test "$foo" -eq "$bar"
$ echo $?
1
All the test command does is return a zero on true tests and a non-zero value on false tests. This allows you to use the test command in if and while statements.
$ if test $foo -eq $bar
> then
> echo "foo is equal to bar"
> else
> echo "foo and bar are different"
> fi
foo and bar are different
$
So what's [...]?
$ ls -il /bin/test /bin/[
10958 -rwxr-xr-x 2 root wheel 18576 May 28 22:27 /bin/[
10958 -rwxr-xr-x 2 root wheel 18576 May 28 22:27 /bin/test
That first column is the inode. If two files have the same inode, they are hard linked and refer to the same file. Notice that they're both 18,576 bytes on my system.
The [ is just another way to do the test command itself. Both of these lines are the same:
if test $foo -eq $bar
if [ $foo -eq $bar ]
The [ was created to make shell scripts look a wee bit nicer. However, underneath, it's running a test command which will return either a zero or non-zero value that if and while can use.
By the way, in Kornshell and BASH, both [ and test are builtin to the shell. But, they do reference the same builtin command.
The [[...]] is a special version of the test that first appeared in Kornshell. It allows for the use of pattern matches and it's a bit faster since it doesn't spawn another process to run the test command.
Code explanations :
IFS= set IFS to null (input fiels separator)
read command that reads the current line with the following options :
-r : do not allow backslashes to escape any characters
-d continue until the first character of DELIM is read, rather than newline
$'\0' zero byte (the ASCII NUL character)
See
help read
No. The test command and [[ ...]] expression are merely constructs that supply an exit status (the only thing while cares about) that reflects the truth of the conditional expression it contains.
For example, the exit status of [[ $foo = bar ]] is 0 (success) if $foo has the value bar, and a non-zero exist status (specifically 1) otherwise.
A while loop executes its body as long as the exit status of the command following the while keyword is 0. In this code, read has a 0 exit status as long as it can read something from its input.

Shell script to check if file exists

I'm trying to write a simple script that will tell me if a filename exist in $Temp that starts with the string "Test".
For example, I have these files
Test1989.txt
Test1990.txt
Test1991.txt
Then I just want to echo that a file was found.
For example, something like this:
file="home/edward/bank1/fiche/Test*"
if test -s "$file"
then
echo "found one"
else
echo "found none"
fi
But this doesn't work.
One approach:
(
shopt -s nullglob
files=(/home/edward/bank1/fiche/Test*)
if [[ "${#files[#]}" -gt 0 ]] ; then
echo found one
else
echo found none
fi
)
Explanation:
shopt -s nullglob will cause /home/edward/bank1/fiche/Test* to expand to nothing if no file matches that pattern. (Without it, it will be left intact.)
( ... ) sets up a subshell, preventing shopt -s nullglob from "escaping".
files=(/home/edward/bank1/fiche/Test*) puts the file-list in an array named files. (Note that this is within the subshell only; files will not be accessible after the subshell exits.)
"${#files[#]}" is the number of elements in this array.
Edited to address subsequent question ("What if i also need to check that these files have data in them and are not zero byte files"):
For this version, we need to use -s (as you did in your question), which also tests for the file's existence, so there's no point using shopt -s nullglob anymore: if no file matches the pattern, then -s on the pattern will be false. So, we can write:
(
found_nonempty=''
for file in /home/edward/bank1/fiche/Test* ; do
if [[ -s "$file" ]] ; then
found_nonempty=1
fi
done
if [[ "$found_nonempty" ]] ; then
echo found one
else
echo found none
fi
)
(Here the ( ... ) is to prevent file and found_file from "escaping".)
You have to understand how Unix interprets your input.
The standard Unix shell interpolates environment variables, and what are called globs before it passes the parameters to your program. This is a bit different from Windows which makes the program interpret the expansion.
Try this:
$ echo *
This will echo all the files and directories in your current directory. Before the echo command acts, the shell interpolates the * and expands it, then passes that expanded parameter back to your command. You can see it in action by doing this:
$ set -xv
$ echo *
$ set +xv
The set -xv turns on xtrace and verbose. Verbose echoes the command as entered, and xtrace echos the command that will be executed (that is, after the shell expansion).
Now try this:
$ echo "*"
Note that putting something inside quotes hides the glob expression from the shell, and the shell cannot expand it. Try this:
$ foo="this is the value of foo"
$ echo $foo
$ echo "$foo"
$ echo '$foo'
Note that the shell can still expand environment variables inside double quotes, but not in single quotes.
Now let's look at your statement:
file="home/edward/bank1/fiche/Test*"
The double quotes prevent the shell from expanding the glob expression, so file is equal to the literal home/edward/bank1/finche/Test*. Therefore, you need to do this:
file=/home/edward/bank1/fiche/Test*
The lack of quotes (and the introductory slash which is important!) will now make file equal to all files that match that expression. (There might be more than one!). If there are no files, depending upon the shell, and its settings, the shell may simply set file to that literal string anyway.
You certainly have the right idea:
file=/home/edward/bank1/fiche/Test*
if test -s $file
then
echo "found one"
else
echo "found none"
fi
However, you still might get found none returned if there is more than one file. Instead, you might get an error in your test command because there are too many parameters.
One way to get around this might be:
if ls /home/edward/bank1/finche/Test* > /dev/null 2>&1
then
echo "There is at least one match (maybe more)!"
else
echo "No files found"
fi
In this case, I'm taking advantage of the exit code of the ls command. If ls finds one file it can access, it returns a zero exit code. If it can't find one matching file, it returns a non-zero exit code. The if command merely executes a command, and then if the command returns a zero, it assumes the if statement as true and executes the if clause. If the command returns a non-zero value, the if statement is assumed to be false, and the else clause (if one is available) is executed.
The test command works in a similar fashion. If the test is true, the test command returns a zero. Otherwise, the test command returns a non-zero value. This works great with the if command. In fact, there's an alias to the test command. Try this:
$ ls -li /bin/test /bin/[
The i prints out the inode. The inode is the real ID of the file. Files with the same ID are the same file. You can see that /bin/test and /bin/[ are the same command. This makes the following two commands the same:
if test -s $file
then
echo "The file exists"
fi
if [ -s $file ]
then
echo "The file exists"
fi
You can do it in one line:
ls /home/edward/bank1/fiche/Test* >/dev/null 2>&1 && echo "found one" || echo "found none"
To understand what it does you have to decompose the command and have a basic awareness of boolean logic.
Directly from bash man page:
[...]
expression1 && expression2
True if both expression1 and expression2 are true.
expression1 || expression2
True if either expression1 or expression2 is true.
[...]
In the shell (and in general in unix world), the boolean true is a program that exits with status 0.
ls tries to list the pattern, if it succeed (meaning the pattern exists) it exits with status 0, 2 otherwise (have a look at ls man page for details).
In our case there are actually 3 expressions, for the sake of clarity I will put parenthesis, although they are not needed because && has precedence on ||:
(expression1 && expression2) || expression3
so if expression1 is true (ie: ls found the pattern) it evaluates expression2 (which is just an echo and will exit with status 0). In this case expression3 is never evaluate because what's on the left site of || is already true and it would be a waste of resources trying to evaluate what's on the right.
Otherwise, if expression1 is false, expression2 is not evaluated but in this case expression3 is.
for entry in "/home/loc/etc/"/*
do
if [ -s /home/loc/etc/$entry ]
then
echo "$entry File is available"
else
echo "$entry File is not available"
fi
done
Hope it helps
The following script will help u to go to a process if that script exist in a specified variable,
cat > waitfor.csh
#!/bin/csh
while !( -e $1 )
sleep 10m
end
ctrl+D
here -e is for working with files,
$1 is a shell variable,
sleep for 10 minutes
u can execute the script by ./waitfor.csh ./temp ; echo "the file exits"
One liner to check file exist or not -
awk 'BEGIN {print getline < "file.txt" < 0 ? "File does not exist" : "File Exists"}'
Wildcards aren't expanded inside quoted strings. And when wildcard is expanded, it's returned unchanged if there are no matches, it doesn't expand into an empty string. Try:
output="$(ls home/edward/bank1/fiche/Test* 2>/dev/null)"
if [ -n "$output" ]
then echo "Found one"
else echo "Found none"
fi
If the wildcard expanded to filenames, ls will list them on stdout; otherwise it will print an error on stderr, and nothing on stdout. The contents of stdout are assigned to output.
if [ -n "$output" ] tests whether $output contains anything.
Another way to write this would be:
if [ $(ls home/edward/bank1/fiche/Test* 2>/dev/null | wc -l) -gt 0 ]

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}"

Echoing the last command run in Bash?

I am trying to echo the last command run inside a bash script. I found a way to do it with some history,tail,head,sed which works fine when commands represent a specific line in my script from a parser standpoint. However under some circumstances I don't get the expected output, for instance when the command is inserted inside a case statement:
The script:
#!/bin/bash
set -o history
date
last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
echo "last command is [$last]"
case "1" in
"1")
date
last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
echo "last command is [$last]"
;;
esac
The output:
Tue May 24 12:36:04 CEST 2011
last command is [date]
Tue May 24 12:36:04 CEST 2011
last command is [echo "last command is [$last]"]
[Q] Can someone help me find a way to echo the last run command regardless of how/where this command is called within the bash script?
My answer
Despite the much appreciated contributions from my fellow SO'ers, I opted for writing a run function - which runs all its parameters as a single command and display the command and its error code when it fails - with the following benefits:
-I only need to prepend the commands I want to check with run which keeps them on one line and doesn't affect the conciseness of my script
-Whenever the script fails on one of these commands, the last output line of my script is a message that clearly displays which command fails along with its exit code, which makes debugging easier
Example script:
#!/bin/bash
die() { echo >&2 -e "\nERROR: $#\n"; exit 1; }
run() { "$#"; code=$?; [ $code -ne 0 ] && die "command [$*] failed with error code $code"; }
case "1" in
"1")
run ls /opt
run ls /wrong-dir
;;
esac
The output:
$ ./test.sh
apacheds google iptables
ls: cannot access /wrong-dir: No such file or directory
ERROR: command [ls /wrong-dir] failed with error code 2
I tested various commands with multiple arguments, bash variables as arguments, quoted arguments... and the run function didn't break them. The only issue I found so far is to run an echo which breaks but I do not plan to check my echos anyway.
Bash has built in features to access the last command executed. But that's the last whole command (e.g. the whole case command), not individual simple commands like you originally requested.
!:0 = the name of command executed.
!:1 = the first parameter of the previous command
!:4 = the fourth parameter of the previous command
!:* = all of the parameters of the previous command
!^ = the first parameter of the previous command (same as !:1)
!$ = the final parameter of the previous command
!:-3 = all parameters in range 0-3 (inclusive)
!:2-5 = all parameters in range 2-5 (inclusive)
!! = the previous command line
etc.
So, the simplest answer to the question is, in fact:
echo !!
...alternatively:
echo "Last command run was ["!:0"] with arguments ["!:*"]"
Try it yourself!
echo this is a test
echo !!
In a script, history expansion is turned off by default, you need to enable it with
set -o history -o histexpand
The command history is an interactive feature. Only complete commands are entered in the history. For example, the case construct is entered as a whole, when the shell has finished parsing it. Neither looking up the history with the history built-in (nor printing it through shell expansion (!:p)) does what you seem to want, which is to print invocations of simple commands.
The DEBUG trap lets you execute a command right before any simple command execution. A string version of the command to execute (with words separated by spaces) is available in the BASH_COMMAND variable.
trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
…
echo "last command is $previous_command"
Note that previous_command will change every time you run a command, so save it to a variable in order to use it. If you want to know the previous command's return status as well, save both in a single command.
cmd=$previous_command ret=$?
if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fi
Furthermore, if you only want to abort on a failed commands, use set -e to make your script exit on the first failed command. You can display the last command from the EXIT trap.
set -e
trap 'echo "exit $? due to $previous_command"' EXIT
Note that if you're trying to trace your script to see what it's doing, forget all this and use set -x.
After reading the answer from Gilles, I decided to see if the $BASH_COMMAND var was also available (and the desired value) in an EXIT trap - and it is!
So, the following bash script works as expected:
#!/bin/bash
exit_trap () {
local lc="$BASH_COMMAND" rc=$?
echo "Command [$lc] exited with code [$rc]"
}
trap exit_trap EXIT
set -e
echo "foo"
false 12345
echo "bar"
The output is
foo
Command [false 12345] exited with code [1]
bar is never printed because set -e causes bash to exit the script when a command fails and the false command always fails (by definition). The 12345 passed to false is just there to show that the arguments to the failed command are captured as well (the false command ignores any arguments passed to it)
I was able to achieve this by using set -x in the main script (which makes the script print out every command that is executed) and writing a wrapper script which just shows the last line of output generated by set -x.
This is the main script:
#!/bin/bash
set -x
echo some command here
echo last command
And this is the wrapper script:
#!/bin/sh
./test.sh 2>&1 | grep '^\+' | tail -n 1 | sed -e 's/^\+ //'
Running the wrapper script produces this as output:
echo last command
history | tail -2 | head -1 | cut -c8-
tail -2 returns the last two command lines from history
head -1 returns just first line
cut -c8- returns just command line, removing PID and spaces.
There is a racecondition between the last command ($_) and last error ( $?) variables. If you try to store one of them in an own variable, both encountered new values already because of the set command. Actually, last command hasn't got any value at all in this case.
Here is what i did to store (nearly) both informations in own variables, so my bash script can determine if there was any error AND setting the title with the last run command:
# This construct is needed, because of a racecondition when trying to obtain
# both of last command and error. With this the information of last error is
# implied by the corresponding case while command is retrieved.
if [[ "${?}" == 0 && "${_}" != "" ]] ; then
# Last command MUST be retrieved first.
LASTCOMMAND="${_}" ;
RETURNSTATUS='✓' ;
elif [[ "${?}" == 0 && "${_}" == "" ]] ; then
LASTCOMMAND='unknown' ;
RETURNSTATUS='✓' ;
elif [[ "${?}" != 0 && "${_}" != "" ]] ; then
# Last command MUST be retrieved first.
LASTCOMMAND="${_}" ;
RETURNSTATUS='✗' ;
# Fixme: "$?" not changing state until command executed.
elif [[ "${?}" != 0 && "${_}" == "" ]] ; then
LASTCOMMAND='unknown' ;
RETURNSTATUS='✗' ;
# Fixme: "$?" not changing state until command executed.
fi
This script will retain the information, if an error occured and will obtain the last run command. Because of the racecondition i can not store the actual value. Besides, most commands actually don't even care for error noumbers, they just return something different from '0'. You'll notice that, if you use the errono extention of bash.
It should be possible with something like a "intern" script for bash, like in bash extention, but i'm not familiar with something like that and it wouldn't be compatible as well.
CORRECTION
I didn't think, that it was possible to retrieve both variables at the same time. Although i like the style of the code, i assumed it would be interpreted as two commands. This was wrong, so my answer devides down to:
# Because of a racecondition, both MUST be retrieved at the same time.
declare RETURNSTATUS="${?}" LASTCOMMAND="${_}" ;
if [[ "${RETURNSTATUS}" == 0 ]] ; then
declare RETURNSYMBOL='✓' ;
else
declare RETURNSYMBOL='✗' ;
fi
Although my post might not get any positive rating, i solved my problem myself, finally.
And this seems appropriate regarding the intial post. :)

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