Meaning of "! -S" in shell script - shell

I am new to shell scripting, and I have encountered a script I didn't understand:
DOWN=true
while $DOWN; do
sleep 0.1
DOWN=false
for i in {1..7}
do
if [ ! -S "qdata/c$i/tm.ipc" ]; then
DOWN=true
fi
done
done
Specifically, what does this command mean:
! -S "qdata/c$i/tm.ipc"

The command you are looking at is actually this:
[ ! -S "qdata/c$i/tm.ipc" ]
Although it looks like punctuation, [ is actually the name of a command, also called test; so the command can also be written like this:
test ! -S "qdata/c$i/tm.ipc"
Which in context would look like this:
if test ! -S "qdata/c$i/tm.ipc"; then
DOWN=true
fi
As the name suggests, its job is to test some attribute of a string, number, or file, and return 0 (which represents true in shell scripts) if the test passes, and 1 (which represents false) if it doesn't.
Armed with this knowledge, you can run man test, and find the following explanations of the ! and -S arguments:
! EXPRESSION
EXPRESSION is false
and
-S FILE
FILE exists and is a socket
So test ! -S filename or [ ! -S filename ] can be read as "not is-socket filename".
So the command is checking whether a "socket" (a special kind of file) exists with each name in the loop. The script uses this command as the argument to an if statement (which can take any command, not just [) and sets DOWN to true if any of them does not exist.

You didn't really specify which shell you're talking about since they can vary a lot.
To answer you question, the context is a common construct
if [ <some test> ]
then
<commands>
fi
Where the [ <some test>] is a call to the command test
If you look at the documentation of that command you can see that ! negates the result and -S checks for True if file is a socket..
So you can read it as if "qdata/c$i/tm.ipc" is not a socket

Related

Checking if a file is empty in unix

I'm trying to do something like
for i in {1..999}
do
#some commands here
if [ ! -s text${i}.txt ]
then
break
fi
done
But the file text${i} is not empty and this loop keeps exiting after going through it once but it should keep going and when I do
for i in {1..999}
do
#some commands here
if [ -s text${i}.txt ]
then
break
fi
done
The program just runs forever.
-s is for testing whether or not a file is empty, not -f. See here for some documentation.

What does -f mean in bash

I was looking at how to use runit to run gunicorn. I was looking at the bash file and I don't know what -f $PID does in
#!/bin/sh
GUNICORN=/usr/local/bin/gunicorn
ROOT=/path/to/project
PID=/var/run/gunicorn.pid
APP=main:application
if [ -f $PID ]; then rm $PID; fi
cd $ROOT
exec $GUNICORN -c $ROOT/gunicorn.conf.py --pid=$PID $APP
Google is useless in this case because searching for flags is useless
Google is useless in this case because searching for flags is useless
Fortunately, the Bash Reference Manual is available online, at http://www.gnu.org/software/bash/manual/bashref.html. It's the first hit when you Google for "Bash manual". ยง6.4 "Bash Conditional Expressions" says:
-f file
True if file exists and is a regular file.
-f - file is a regular file (not a directory or device file)
Check this out for all file test operators:
http://tldp.org/LDP/abs/html/fto.html
The [ is the same as the command test which allows you to test certain things. Try help test to find out what the flags are. Things to be careful with are spaces - the [ needs a space after it.
-f checks if the file exists and is a regular file.
[ -f "$var" ]
Checks if $var is an existing file (regular file). Symbolic link passes this test too.

Difference between file tests in Bash

I am troubleshooting an existing Bash script and in the script it has two tests:
if [ ! -s <file_location> ] ; then
# copy the file to the file_location
if [ -s <file_location> ] ; then
# operate on the file
fi
fi
According to the Bash Tutorial, -s tests if the file is not of zero size. Would it be better to replace the ! -s test with a -e ? I could understand the second, nested test being a -s but the first one looks like it could be replaced with -e. What is the advantage here of ! -s vs -e? Am I missing something?
If the file exists but is empty, -e would pass, but the file would likely be useless. Using ! -s ensures that the file is present and contains useful content.

Make fails checking if directory exists

I googled for this, but I can't figure out why Bash complains with the following code to check if a directory exists:
test.mk
#!/bin/bash
MYDIR="dl"
all:
if [ ! -d $MYDIR ]; then
#if [ ! -d "${MYDIR}" ]; then
#if [ ! -d ${MYDIR} ]; then
#Here
fi
make -f test.mk
if [ ! -d YDIR ]; then
/bin/sh: Syntax error: end of file unexpected
make: *** [all] Error 2
Does someone know why it fails? And why does it call /bin/sh instead of /bin/bash? Thank you.
Edit: unlike Bash, make doesn't support multi-line block. Here's working code:
MYDIR="dl"
all:
if [ ! -d ${MYDIR} ]; then\
echo "Here";\
else\
echo "There";\
fi
The #!/bin/bash shebang that you inserted at top is useless, and it is treated by make as a comment.
make sends by default commands to /bin/sh. To specify a different shell, use the macro SHELL = /bin/bash.
Moreover, you need to escape your variable:
if [ ! -d ${MYDIR} ]
I'm not sure if make can handle multi-line statements, so try to put all the if block in a line.
if [ ! -d ${MYDIR} ]; then DO_SOMETHING; DO_SOMETHING_ELSE; fi
You're feeding test.mk to make, not to bash. Then make sends individual lines to the shell, not whole blocks.
make uses its SHELL macro to determine which shell to use. You can override it to make it use bash.
The reason why you're getting YDIR is that make has silly rules about variable interpolation. Write $(MYDIR), not $MYDIR.
try bracing your variable:
${MYDIR}

Sourcing a script file in bash before starting an executable

I'm trying to write a bash script that "wraps" whatever the user wants to invoke (and its parameters) sourcing a fixed file just before actually invoking it.
To clarify: I have a "ConfigureMyEnvironment.bash" script that must be sourced before starting certain executables, so I'd like to have a "LaunchInMyEnvironment.bash" script that you can use as in:
LaunchInMyEnvironment <whatever_executable_i_want_to_wrap> arg0 arg1 arg2
I tried the following LaunchInMyEnvironment.bash:
#!/usr/bin/bash
launchee="$#"
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
exec "$launchee"
where I have to use the "launchee" variable to save the $# var because after executing source, $# becomes empty.
Anyway, this doesn't work and fails as follows:
myhost $ LaunchInMyEnvironment my_executable -h
myhost $ /home/me/LaunchInMyEnvironment.bash: line 7: /home/bin/my_executable -h: No such file or directory
myhost $ /home/me/LaunchInMyEnvironment.bash: line 7: exec: /home/bin/my_executable -h: cannot execute: No such file or directory
That is, it seems like the "-h" parameter is being seen as part of the executable filename and not as a parameter... But it doesn't really make sense to me.
I tried also to use $* instead of $#, but with no better outcoume.
What I'm doing wrong?
Andrea.
Have you tried to remove double quotes in exec command?
Try this:
#!/usr/bin/bash
typeset -a launchee
launchee=("$#")
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
exec "${launchee[#]}"
That will use arrays for storing arguments, so it will handle even calls like "space delimited string" and "string with ; inside"
Upd: simple example
test_array() { abc=("$#"); for x in "${abc[#]}"; do echo ">>$x<<"; done; }
test_array "abc def" ghi
should give
>>abc def<<
>>ghi<<
You might want to try this (untested):
#!/usr/bin/bash
launchee="$1"
shift
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
exec "$launchee" $#
The syntax for exec is exec command [arguments], however becuase you've quoted $launchee, this is treated as a single argument - i.e., the command, rather than a command and it's arguments. Another variation may be to simply do: exec $#
Just execute it normally without exec
#!/usr/bin/bash
launchee="$#"
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
$launchee
Try dividing your list of argumets:
ALL_ARG="${#}"
Executable="${1}"
Rest_of_Args=${ALL_ARG##$Executable}
And try then:
$Executable $Rest_of_Args
(or exec $Executable $Rest_of_Args)
Debugger

Resources