problems with checking for a directory in bash shell script - bash

I am writing a batch-processing script bash that needs to first check to see if a folder exists to know whether or not to run a certain python script that will create and populate the folder. I have done similar things before that do fine with changing the directories and finding directories from a stored variable, but for some reason I am just missing something here.
Here is roughly how the script is working.
if [ -d "$net_output" ]
then
echo "directory exists"
else
echo "directory does not exist"
fi
when I run this script, I usually echo $net_output in the line before to see what it will evaluate to. When the script runs I get my else block of code saying "Directory does not exist", but when I then copy and paste the $net_output directory path that is echoed before into the shell terminal, it changes directories just fine, proving that the directory does in fact exist. I am using Ubuntu 12.04 on a Dell machine.
Thank you in advance for any help that someone can offer. Let me know what additional information I can provide.

The most common cases I've encountered when someone posts a problem like this are the following:
1. The variable contains literal quotes. Bash does not recursively parse quotes, it only parses the "outer" quotes given on the command line.
$ mkdir "/tmp/dir with spaces"
$ var='"/tmp/dir with spaces"'
$ echo "$var"
"/tmp/dir with spaces"
$ [ -d "/tmp/dir with spaces" ]; echo $?
0
$ [ -d "$var" ]; echo $? # equivalent to [ -d '"/tmp/dir with spaces"' ]
1
2. The variable contains a relative path, and the current directory is not what you expected. Check that the value of echo "$PWD" outputs what you expected.
3. The variable was read from a file with dos line endings, CRLF (\r\n). Unix and unix-like systems use just LF (\n) for line endings. If that's the case, the path will contain a CR (\r) at the end. A CR at the end of a line will be "invisible" in a terminal. Check with printf '%q\n' "$var" while debugging the script. See BashFAQ 52 on how to get rid of them.

Related

Take a file name as input and check if it exists

How do I create a Bash script that takes a file name as input? Then, if that file exists, it should print "File exists"; if not, print "File does not exist".
For example, if I ran ./do-i-exist.sh ./do-i-exist.sh, the output should be only 'File exists'
file="$1"
read answer
if [ $file != -$2 ]
then
echo "File exists"
else
echo "File does not exist"
fi
This is what I'm working with but is not working for me, whenever I add an extension like .sh, .txt or something similar it won't find the file.
The test if a file exists can be done like this
if [ -f "$file" ]
then
This tests for a regular file, not for other kinds of files like a directory.
This is how you can do it. Pass the name of the file while like ./do-i-exist.sh file_path.
if [ -f "$1" ]
then
echo "File Exists"
else
echo "File does not exist"
fi
First of all, I want to thank anyone and everyone who tried to help. After 3 hard working days, I found the answer, here it is:
#!/bin/bash
file="$#"
if [ -f $file ]
then
echo "File exists"
else
echo "File does not exist"
fi
Using this table:
Variable Name
Description
$0
The name of the Bash script
$1 - $9
The first 9 arguments to the Bash script
$#
Number of arguments passed to the Bash script
$#
All arguments passed to the Bash script
$?
The exit status of the most recently run process
$$
The process ID of the current script
$USER
The username of the user running the script
$HOSTNAME
The hostname of the machine
$RANDOM
A random number
$LINENO
The current line number in the script
I and other users were focused on using $1 from my understanding this refers to the first argument passed to the script but for some reason, it wasn't working since it needed to pass more inputs.
As from my previous comments I didn't have control over the input. The input was hidden in a locked file, and I needed to feed my script to it.
From what we know $0 is only used to check for the file names, $1 to get the first statement and $# will just take anything(I guess).
I know absolutely nothing about bash and it was the first time ever using it, which is why it took me 3 days to solve this puzzle. This was part of a CTF and just like me, many others may struggle in the future to understand or know how to make a script that will just adapt to a series of inputs from a second script.
This is how it was supported to work:
I was given access to a very restricted server and on this server, I was given the encrypted-file.sh file. This file was supposed to be fed to /path/to/myfile.sh then encrypted-file.sh would execute a second command to open a third locked file hiding a flag on it.
This only works with the right bash file using the right variables on it for encrypted-file.sh to run without errors, which is what I accomplished here.
I used a while loop because it made sense in my case because I really needed a file for the script to work.
restore_file="$1"
while [ ! -f "$restore_file" ]
do
echo "File not found: $restore_file"
echo "Please provide a valid file:"
read restore_file
done
As written above, $1 is the first argument given to the script. In this case if no argument is given or that is not a file, it will prompt again.
By the way, use -d instead of -f to check for a directory.

Why basename can not be used for a variable?

I have the following shell script
#!/bin/bash
echo "$(basename $(pwd))"
MYDIR= "$(basename $(pwd))"
echo "this is ${MYDIR}"
When I execute it I got
mydirectory
./test.sh: line 4: mydirectory: command not found
this is
so eventhough the first line gets my current directory somehow this cannot be assigned to a variable
Why?? and how can I assign correctly the current directory to a variable (not the complete path)
EDIT: After I tried Gilles Quenot answer that works! (Thanks!) I tried mine with a small variation
#!/bin/bash
echo "$(basename $(pwd))"
MYDIR="$(basename $(pwd))"
echo "this is ${MYDIR}"
and now it works! turns out I should not put spaces around the "="!
when you have shell errors, always check your script on https://shellcheck.net/
never put spaces around = in shell
for dir name, use dirname
avoid using UPPER CASE variables, they are reserved for system use
better use already configured $PWD variable:
:
echo "$(basename "$PWD")"
mydir="$(dirname "$PWD")"
echo "this is $mydir"

How do I tell if a variable is a file in bash

I am not a great bash scripter and hence have a few questions. One of which is how (or even whether) bash understands that a variable is a "file" or simply a local variable.
file=/usr/share/lib
Obviously this is a file to be saved, etc and can be used like so:
echo "$output" > $file
To save the output of $output to $file.
But where in bash does it calculate whether it's a file or not, is it only a file once it's been passed to a 'writing method'?
If you treat that variable as a file name, bash will simply do what it's told e.g.
echo "Test output" > $file
will work regardless of file being set to /tmp/myfile.txt, or to abcd. In the above you're using bash's file redirection to write out the standard out to the file you've named.
Consequently if you use the wrong variable in the above pattern, or have the value set incorrectly, bash will simply follow your instructions and you may end up with incorrectly named/located files.
You need to check this yourself, bash is only aware of the contents of the variable. If you want to check if a file location is held within a variable, you can test for it using the -f test operator
if [ -f "$file" ]
then
echo "$file is a file"
echo "$output" > "$file"
else
echo "$file is not a file"
fi
Variables are strings - nothing more, nothing less. (I'm ignoring array variables here, WLOG).
Bash (or any shell) expands variables blindly, without considering what you intend to do with them. It's only in the next stage of command processing that the contents of the string matter.
To use your example:
output="foo bar"
file=/usr/share/lib
echo "$output" >"$file"
(I've quoted $file even though it's not necessary here, simply because I've been bitten too many times by changing the value and breaking everything).
The line
echo "$output" >"$file"
gets transformed into
echo "foo bar" >"/usr/share/lib"
and only then does bash consider the > and attempt to open /usr/share/lib for writing.

bash backup script error

So I'm writing a simple backup script that when called will either back up a specific file or all the files in my current directory into my backup directory.
This is my script
#!/bin/bash
#verify if $1 is empty, if so, copy all content to backup directory
if [ -z "$1" ]
then
$files=ls
#Looping through files
for file in $files
do
cp $file ../backup/
done
#else copy files specified
else
$files=ls $1
#Looping through files
for file in $files
do
cp $file ../backup/
done
fi
and the only error I'm getting is:
./backup: line 7: =ls: command not found
I'm not sure why the script won't recognize ls as a command. Any ideas?
to assign a variable, you don't need the dollar sign:
files=foo
to save the output of an command to a var, you need do:
files=$(ls)
or
files=$(ls /path/to/some/dir)
I see two mistakes:
When you initialize variable "files" you should not prepend it with "$" symbol
Command ls should be placed in back quotes (`):
This is a short example:
#!/bin/bash
files=`ls`
for file in $files
do
echo "file: $file"
done
You should try putting the ls into ` marks - better, the back quote. Like this:
files=`ls`
Here a little background. Check this page for more information about quotation marks in the shell environment:
The back quote is not used for quoting characters. That character is
used for command substitution, where the characters between them are
executed by the shell and the results is inserted on that line.
Example:
echo the date is `date`

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