Loop outputting blank variables - SSH Bash - bash

I am writing a script to help me sync files between my home pc and my server. One of the problems ive run into is that, when I run the for loop, it outputs blank lines.
Everything else seems to be fine, echo $REMOTE lists all files and their full paths as expected. I have tried unquoting, quoting, Changing EOF from "EOF" to EOF etc, but so far nothing has worked.
Here is the script below:
function uploadDownload()
{
if [ "$1" == "d" ]; then
ssh -i ~/Dropbox/Business/aws/first.pem ubuntu#XXXX.194.202 << EOF
echo $REMOTE
for file in $REMOTE; do
echo $file
done
EOF
#scp -r -i ~/Dropbox/Business/aws/first.pem ubuntu#XXXX.194.202:$REMOTE $LOCAL
elif [ "$1" == "u" ]; then
scp -r -i ~/Dropbox/Business/aws/first.pem $LOCAL ubuntu#XXXX.194.202:$REMOTE
fi
}
if [ "$2" == "m" ]; then
if [ -z "$3" ]; then
echo "Please enter the local location of the files and provide an absolute path..."
read LOCAL
echo "Please enter the remote location of the files..."
read REMOTE
uploadDownload
else
LOCAL=$2
if [ -z "$4" ]; then
REMOTE=$3
else
REMOTE='ubuntu#XXXX.194.202:~/test/'
fi
uploadDownload
fi
else
LOCAL='/home/will/Dropbox/Business/aws/files/binaryhustle/'
REMOTE='/home/ubuntu/dev/binaryhustle/*'
uploadDownload $1
fi
EDIT: based on advice I have edited the uploadDownload function. I should also note than I tested echo "$REMOTE"; outside of the loop and it returns nothing (unlike without quotes, where it returns something).
function uploadDownload()
{
if [ "$1" == "d" ]; then
ssh -i ~/Dropbox/Business/aws/first.pem ubuntu#54.252.194.202 << EOF
for file in "/home/ubuntu/dev/binaryhustle"; do
echo $file
done
EOF
#scp -r -i ~/Dropbox/Business/aws/first.pem ubuntu#54.252.194.202:$REMOTE $LOCAL
elif [ "$1" == "u" ]; then
scp -r -i ~/Dropbox/Business/aws/first.pem $LOCAL ubuntu#54.252.194.202:$REMOTE
fi
}

Try to quote your variable $file as it is expanded before being sent to the remote server:
echo $file
To
echo \$file
Or better yet
echo \"\$file\"

You may want to take a look at unison. Its powerful file syncing tool able to work over ssh. It doesn't do any content diff though.

Credit to #konsolebox for getting me half way there, I got it working by adding the slash to file, however this did not work with << EOF , only <
function uploadDownload()
{
if [ "$1" == "d" ]; then
ssh -i ~/Dropbox/Business/aws/first.pem ubuntu#54.252.194.202 <<EOF
for file in "$REMOTE"; do
echo \$file
done
EOF
#scp -r -i ~/Dropbox/Business/aws/first.pem ubuntu#54.252.194.202:$REMOTE $LOCAL
elif [ "$1" == "u" ]; then
scp -r -i ~/Dropbox/Business/aws/first.pem $LOCAL ubuntu#54.252.194.202:$REMOTE
fi
}

Related

UNIX how to make my script delete multiple files and wildcards?

I was given the task of making a remove script that imitates the rm command. As you know, the rm command deletes all files if you were to type something like rm file1 file2. Using this example, my script would only delete file2. Can anyone help me on how to make it so my remove script would delete all files listed? My script is below. I apologise if its a little messy, I am new to coding.
#!/bin/bash
function directory(){
#Checks if deleted directory & .restore.info file exists
#If they don't exist, it creates them
if [ ! -d ~/deleted ]
then
mkdir ~/deleted
fi
if [ ! -f ~/.restore.info ]
then
touch ~/.restore.info
fi
}
function movefile(){
#not currently using
mv "$1" ~/deleted/$1
echo "file moved to recycle bin"
}
function error_conditions(){
#not currently using
#Prints error messages and checks if file is in project directory
if [ ! -f ~/project ]
then
echo "cannot remove $filename: no such file or directory"
elif [ -d ~/project ]
then
echo "cannot remove $filename: is a directory"
else
echo "missing operand"
fi
}
function delete_file(){
#Gets inode for filename
#Takes user input and puts file wherever based on user input
inode=$(stat -c%i "$filename")
pwd=$(readlink -e $filename)
if "$interactive"
then
if [ "$verbose" = true ]; then
read -p "Are you sure you want to delete $filename? " user_input
if [ $user_input == "y" ] || [ $user_input == "Y" ] || [ $user_input == "yes" ] || [ $user_input == "Yes" ];
then
mv $filename ~/deleted/${filename}_$inode
#moves deleted file to deleted directory (with inode at end)
echo ${filename}_$inode:$pwd>>~/.restore.info
#stores info of removed file in .restore.info (with path)
echo "removed '$filename'"
else
echo "Nothing has been deleted"
fi
else
read -p "Are you sure you want to delete $filename? " user_input
if [ $user_input == "y" ] || [ $user_input == "Y" ] || [ $user_input == "yes" ] || [ $user_input == "Yes"];
then
mv "$filename" ~/deleted/${filename}_$inode
echo ${filename}_$inode:$pwd>>~/.restore.info
else
echo "Aborted"
fi
fi
elif "$verbose"
then
mv "$filename" ~/deleted/${filename}_$inode
echo ${filename}_$inode:$inode:pwd>>~/.restore.info
echo "removed '$filename'"
else
mv "$filename" ~/deleted/${filename}_$inode
echo ${filename}_$inode:$pwd>>~/.restore.info
echo "Executed"
fi
}
#Setting all flags to false
interactive=false
verbose=false
recursive=false
while getopts :ivr optionvar
do
case "$optionvar" in
i) interactive=true;;
v) verbose=true;;
r) recursive=true;;
esac
done
shift $((OPTIND-1)) #process arguments.
#doing error commands with help of recursive
for i in $*
do
filename=$i
basefile=$(basename $i)
if [ "$filename" == " " ];
then
echo "No filename provcided"
elif [ -d $filename ];
then
if [ ! $recursive = true ];
then
echo "Directory name provided, please provide a file"
fi
elif [ ! -f $filename ];
then
echo "File does not exist"
# elif [ "$basefile" == "safe_rm" ]
# then
# echo "Attempting to delete safe_rm"
fi
done
#################################M A I N###############################
directory
delete_file $*
#error_conditions $* #- this gives me duplicate output lines
#movefile "$#" - this gives me an unnecessary "mv: cannot stat" output line
I'm not going to do a detailed code review of your whole script, but here are a few notes.
You are looping over the arguments in the main part of your script, but then you're calling the delete function with multiple arguments. That function has no looping in it. Move the loop from main() to delete_files() (and note that I pluralized its name for clarity).
And speaking of main(), you might as well encapsulate that code (option processing, function dispatch, etc.) in a function of that name, then at the bottom of your script have a line that calls it: main "$#"
Don't use $* unless you need what it does and understand its use - instead use "$#" almost always and always quote it (with very rare exceptions)
Use indentation consistently
If your script doesn't need to be portable to shells other than Bash, then use Bash-specific features such as [[ ]] instead of [ ]
You're using both methods of naming a function at the same time (function f()). Use one or the other - parens are preferred over using function - so f () { ...; }
Use more quotes, some examples:
pwd=$(readlink -e "$filename")
mv "$filename" ~/deleted/"${filename}_$inode"
echo "${filename}_$inode:$pwd" >> ~/.restore.info
But I don't recommend using tilde (~) in scripts - use $HOME instead. And if you need to look up a user's home directory, use getent instead of other methods.

capture output using expect

I want compare the number of files on the remote server and my local directory. I ssh into the server and I was able to capture the output of "ls somewhere/*something | wc -l" using $expect_out(buffer) and store it as a variable. Now my problem is that how do I come back to my local computer and count the files here and compare them. After this comparison, I need to go back to the server and continue the job if the result of the comparison is acceptable.
The easiest thing to do -- including from a correctness perspective -- is to not try to have a single long-running SSH session, but multiple shorter-lived ones (potentially using SSH multiplexing to reuse a single transport between such sessions):
count_remote_files() {
local host=$1 dirname=$2 dirname_q
printf -v dirname_q '%q' "$dirname"
remote_files=$(ssh "$host" "bash -s ${dirname_q}" <<EOF
cd "$1" || exit
set -- *
if [ "$#" -eq 1 ] && [ ! -e "$1" ] && [ ! -L "$1" ]; then
echo "0"
else
echo "$#"
fi
EOF
)
}
count_local_files() {
local dirname=$1
cd "$dirname" || return
set -- *
if [ "$#" -eq 1 ] && [ ! -e "$1" ] && [ ! -L "$1" ]; then
echo "0"
else
echo "$#"
fi
}
if (( $(count_remote_files "$host" "$remote_dir") ==
$(count_local_files "$local_dir") )); then
echo "File count is identical"
else
echo "File count differs"
ssh "$host" 'echo "doing something else now"'
fi
Since you are using Expect, you can easily count the local files by using Tcl commands, since Expect is built on top of Tcl:
set num_local_files [llength [glob somewhere/*something]]
For more info see http://nikit.tcl.tk/page/Tcl+Tutorial+Lesson+25

Deleting a line that contains a string

I'm trying to delete a line that contains a string pass through an argument, but I can't get it to work. I'm on OSX 10.9.
sed -i '' '/$2/d' /etc/hosts
Shouldn't that work? It just keeps the file as is. Nothing changes. My command is sudo hosts remove junior.dev.
Here is my shell script:
#!/bin/sh
let $# || { echo No arguments supplied. Example: hosts add 192.168.2.2 mysite.dev; exit 1; }
if [ $1 = "add" ]; then
if [ -z "$2" ] || [ -z "$3" ]; then
echo "You must supply an IP address and a host name."
exit 1;
else
echo "$2\t$3" >> /etc/hosts
echo "Done."
fi
fi
if [ $1 = "remove" ]; then
if [ -z "$2" ]; then
echo "You must supply a host name."
exit 1;
else
sed -i '' '/$2/d' /etc/hosts
echo "Done."
fi
fi
Use double-quotes ":
$ echo "$foo"
> bar
$ echo '$foo'
> $foo
When using double-quotes ", the variables are expanded, when using single-quotes ', they are not expanded.

Bash script expecting then, when else is needed

I am trying to run a shell script called graphhopper.sh in Ubuntu 12.04 which was given by a website. When I run it, terminal produces
: not found.sh: 2: graphhopper.sh:
graphhopper.sh: 39: graphhopper.sh: Syntax error: "else" unexpected (expecting "then")
The lines which start from 37 in the shell file are,
if [ ${OSM_FILE: -4} == ".pbf" ]; then
wget -O $OSM_FILE $LINK
else
# make sure aborting download does not result in loading corrupt osm file
TMP_OSM=temp.osm
wget -O - $LINK | bzip2 -d > $TMP_OSM
mv $TMP_OSM $OSM_FILE
fi
if [ ! -f "$OSM_FILE" ]; then
echo "ERROR couldn't download or extract OSM file $OSM_FILE ... exiting"
exit
fi
else
echo "## using existing osm file $OSM_FILE"
fi
This is the whole shell script.
#!/bin/bash
GH_HOME=$(dirname $0)
JAVA=$JAVA_HOME/bin/java
if [ "x$JAVA_HOME" = "x" ]; then
JAVA=java
fi
vers=`$JAVA -version 2>&1 | grep "java version" | awk '{print $3}' | tr -d \"`
bit64=`$JAVA -version 2>&1 | grep "64-Bit"`
if [ "x$bit64" != "x" ]; then
vers="$vers (64bit)"
fi
echo "## using java $vers from $JAVA_HOME"
CONFIG=config.properties
if [ ! -f "config.properties" ]; then
cp config-example.properties $CONFIG
fi
ACTION=$1
FILE=$2
USAGE="./graphhopper.sh import|ui|test <your-osm-file>"
if [ "x$ACTION" = "x" ]; then
echo -e "## action $ACTION not found. try \n$USAGE"
fi
function ensureOsmXml {
if [ ! -s "$OSM_FILE" ]; then
echo "File not found '$OSM_FILE'. Press ENTER to get it from: $LINK"
echo "Press CTRL+C if you do not have enough disc space or you don't want to download several MB."
read -e
echo "## now downloading OSM file from $LINK and extracting to $OSM_FILE"
if [ ${OSM_FILE: -4} == ".pbf" ]; then
wget -O $OSM_FILE $LINK
else
# make sure aborting download does not result in loading corrupt osm file
TMP_OSM=temp.osm
wget -O - $LINK | bzip2 -d > $TMP_OSM
mv $TMP_OSM $OSM_FILE
fi
if [ ! -f "$OSM_FILE" ]; then
echo "ERROR couldn't download or extract OSM file $OSM_FILE ... exiting"
exit
# fi
else
echo "## using existing osm file $OSM_FILE"
fi
}
function ensureMaven {
# maven home existent?
if [ "x$MAVEN_HOME" = "x" ]; then
# not existent but probably is maven in the path?
MAVEN_HOME=`mvn -v | grep "Maven home" | cut -d' ' -f3`
if [ "x$MAVEN_HOME" = "x" ]; then
# try to detect previous downloaded version
MAVEN_HOME="$GH_HOME/maven"
if [ ! -f "$MAVEN_HOME/bin/mvn" ]; then
echo "No Maven found in the PATH. Now downloading+installing it to $MAVEN_HOME"
cd "$GH_HOME"
MVN_PACKAGE=apache-maven-3.0.5
wget -O maven.zip http://www.eu.apache.org/dist/maven/maven-3/3.0.5/binaries/$MVN_PACKAGE-bin.zip
unzip maven.zip
mv $MVN_PACKAGE maven
rm maven.zip
fi
fi
fi
}
function packageCoreJar {
if [ ! -f "$JAR" ]; then
echo "## now building graphhopper jar: $JAR"
echo "## using maven at $MAVEN_HOME"
#mvn clean
"$MAVEN_HOME/bin/mvn" -f "$GH_HOME/core/pom.xml" -DskipTests=true install assembly:single > /tmp/graphhopper-compile.log
returncode=$?
if [[ $returncode != 0 ]] ; then
echo "## compilation failed"
cat /tmp/graphhopper-compile.log
exit $returncode
fi
else
echo "## existing jar found $JAR"
fi
}
function prepareEclipse {
ensureMaven
packageCoreJar
cp core/target/graphhopper-*-android.jar android/libs/
}
## now handle actions which do not take an OSM file
if [ "x$ACTION" = "xclean" ]; then
rm -rf */target
exit
elif [ "x$ACTION" = "xeclipse" ]; then
prepareEclipse
exit
elif [ "x$ACTION" = "xandroid" ]; then
prepareEclipse
"$MAVEN_HOME/bin/mvn" -f "$GH_HOME/android/pom.xml" install android:deploy android:run
exit
fi
if [ "x$FILE" = "x" ]; then
echo -e "no file specified? try \n$USAGE"
exit
fi
# NAME = file without extension if any
NAME="${FILE%.*}"
if [ ${FILE: -4} == ".osm" ]; then
OSM_FILE=$FILE
elif [ ${FILE: -4} == ".pbf" ]; then
OSM_FILE=$FILE
elif [ ${FILE: -7} == ".osm.gz" ]; then
OSM_FILE=$FILE
else
# no end default to osm
OSM_FILE=$NAME.osm
fi
GRAPH=$NAME-gh
VERSION=`grep "<name>" -A 1 pom.xml | grep version | cut -d'>' -f2 | cut -d'<' -f1`
JAR=core/target/graphhopper-$VERSION-jar-with-dependencies.jar
# file without path (.osm.gz or osm.bz2 is also possible)
TMP=$(basename "$FILE")
TMP="${TMP%.*}"
TMP="${TMP%.*}"
if [ "x$TMP" = "xunterfranken" ]; then
LINK="http://download.geofabrik.de/openstreetmap/europe/germany/bayern/unterfranken.osm.bz2"
JAVA_OPTS="-XX:PermSize=60m -XX:MaxPermSize=60m -Xmx200m -Xms200m"
elif [ "x$TMP" = "xgermany" ]; then
LINK=http://download.geofabrik.de/openstreetmap/europe/germany.osm.bz2
# Info: for import we need a more memory than for just loading it
JAVA_OPTS="-XX:PermSize=60m -XX:MaxPermSize=60m -Xmx1600m -Xms1600m"
else
LINK=`echo $NAME | tr '_' '/'`
if [ ${FILE: -4} == ".osm" ]; then
LINK="http://download.geofabrik.de/$LINK-latest.osm.bz2"
else
LINK="http://download.geofabrik.de/$LINK-latest.osm.pbf"
fi
if [ "x$JAVA_OPTS" = "x" ]; then
JAVA_OPTS="-XX:PermSize=60m -XX:MaxPermSize=60m -Xmx1000m -Xms1000m"
fi
fi
ensureOsmXml
ensureMaven
packageCoreJar
echo "## now $ACTION. JAVA_OPTS=$JAVA_OPTS"
if [ "x$ACTION" = "xui" ] || [ "x$ACTION" = "xweb" ]; then
export MAVEN_OPTS="$MAVEN_OPTS $JAVA_OPTS"
"$MAVEN_HOME/bin/mvn" -f "$GH_HOME/web/pom.xml" -Dgraphhopper.config=$CONFIG \
-Dgraphhopper.osmreader.osm=$OSM_FILE -Djetty.reload=manual jetty:run
elif [ "x$ACTION" = "ximport" ]; then
"$JAVA" $JAVA_OPTS -cp "$JAR" com.graphhopper.GraphHopper printVersion=true config=$CONFIG \
graph.location="$GRAPH" \
osmreader.osm="$OSM_FILE"
elif [ "x$ACTION" = "xtest" ]; then
"$JAVA" $JAVA_OPTS -cp "$JAR" com.graphhopper.GraphHopper printVersion=true config=$CONFIG \
graph.location="$GRAPH" osmreader.osm="$OSM_FILE" prepare.chShortcuts=false \
graph.testIT=true
elif [ "x$ACTION" = "xmeasurement" ]; then
ARGS="graph.location=$GRAPH osmreader.osm=$OSM_FILE prepare.chShortcuts=fastest osmreader.acceptWay=CAR"
echo -e "\ncreate graph via $ARGS, $JAR"
START=$(date +%s)
"$JAVA" $JAVA_OPTS -cp "$JAR" com.graphhopper.GraphHopper $ARGS prepare.doPrepare=false
END=$(date +%s)
IMPORT_TIME=$(($END - $START))000
function startMeasurement {
COUNT=5000
ARGS="$ARGS prepare.doPrepare=true measurement.count=$COUNT measurement.location=$M_FILE_NAME graph.importTime=$IMPORT_TIME"
echo -e "\nperform measurement via $ARGS, $JAR"
"$JAVA" $JAVA_OPTS -cp "$JAR" com.graphhopper.util.Measurement $ARGS
}
# use all <last_commits> versions starting from HEAD
last_commits=$3
if [ "x$last_commits" = "x" ]; then
# use current version
"$MAVEN_HOME/bin/mvn" -f "$GH_HOME/core/pom.xml" -DskipTests clean install assembly:single
startMeasurement
exit
fi
commits=$(git rev-list HEAD -n $last_commits)
for commit in $commits; do
git checkout $commit -q
M_FILE_NAME=`git log -n 1 --pretty=oneline | grep -o "\ .*" | tr " ,;" "_"`
M_FILE_NAME="measurement$M_FILE_NAME.properties"
echo -e "\nusing commit $commit and $M_FILE_NAME"
"$MAVEN_HOME/bin/mvn" -f "$GH_HOME/core/pom.xml" -DskipTests clean install assembly:single
startMeasurement
done
fi
[Expanded from my comment...] The script file apparently has DOS-style line endings (i.e. carriage return followed by linefeed, instead of just linefeed). This confuses the shell greatly, since it sees the carriage return as part of the command. The giveaway is that first error message:
: not found.sh: 2: graphhopper.sh:
What's actually happened is it printed the error message "graphhopper.sh: 2: graphhopper.sh: ^M: not found" (where the ^M is actually a carriage return); when the terminal sees the ^M it goes back to the beginning of the line, and prints the end of the error message over top of the beginning.
One of the other effects this has is that the shell can't recognize keywords at the end of lines. When it sees a line like:
if [ ${OSM_FILE: -4} == ".pbf" ]; then^M
...it thinks then^M a regular command, not the end of the condition part of the if command, so it keeps looking for a then. But the else command seems to have some spaces at the end:
else ^M
...which means the shell does recognize the else keyword and get very confused about what it's doing in the middle of the condition part of the if.
So what can you do about it? There's almost certainly a command for it; I'm used to dos2unix, but apparently ubuntu doesn't have that, instead the "tofrodos" package includes the command fromdos (see here). Or, you can do it with perl:
perl -pi -e 's/\r//g' graphhopper.sh
Your text editor may also be able to save in unix (rather than DOS) format. Speaking of which, you should either switch your text editor to unix mode, or find a different text editor for scripting.
Remove the extra fi:
if [ ! -f "$OSM_FILE" ]; then
echo "ERROR couldn't download or extract OSM file $OSM_FILE ... exiting"
exit
# fi
else
echo "## using existing osm file $OSM_FILE"
fi
The variable OSM_FILE is not set to anything. This causes it to expand to a empty string, which causes the shell to think there are syntax errors in the if conditions.
Print out the value of OSM_FILE before use, and if it's empty, debug backwards.

Check if passed argument is file or directory in Bash

I'm trying to write an extremely simple script in Ubuntu which would allow me to pass it either a filename or a directory, and be able to do something specific when it's a file, and something else when it's a directory. The problem I'm having is when the directory name, or probably files too, has spaces or other escapable characters are in the name.
Here's my basic code down below, and a couple tests.
#!/bin/bash
PASSED=$1
if [ -d "${PASSED}" ] ; then
echo "$PASSED is a directory";
else
if [ -f "${PASSED}" ]; then
echo "${PASSED} is a file";
else
echo "${PASSED} is not valid";
exit 1
fi
fi
And here's the output:
andy#server~ $ ./scripts/testmove.sh /home/andy/
/home/andy/ is a directory
andy#server~ $ ./scripts/testmove.sh /home/andy/blah.txt
/home/andy/blah.txt is a file
andy#server~ $ ./scripts/testmove.sh /home/andy/blah\ with\ a\ space.txt
/home/andy/blah with a space.txt is not valid
andy#server~ $ ./scripts/testmove.sh /home/andy\ with\ a\ space/
/home/andy with a space/ is not valid
All of those paths are valid, and exist.
That should work. I am not sure why it's failing. You're quoting your variables properly. What happens if you use this script with double [[ ]]?
if [[ -d $PASSED ]]; then
echo "$PASSED is a directory"
elif [[ -f $PASSED ]]; then
echo "$PASSED is a file"
else
echo "$PASSED is not valid"
exit 1
fi
Double square brackets is a bash extension to [ ]. It doesn't require variables to be quoted, not even if they contain spaces.
Also worth trying: -e to test if a path exists without testing what type of file it is.
At least write the code without the bushy tree:
#!/bin/bash
PASSED=$1
if [ -d "${PASSED}" ]
then echo "${PASSED} is a directory";
elif [ -f "${PASSED}" ]
then echo "${PASSED} is a file";
else echo "${PASSED} is not valid";
exit 1
fi
When I put that into a file "xx.sh" and create a file "xx sh", and run it, I get:
$ cp /dev/null "xx sh"
$ for file in . xx*; do sh "$file"; done
. is a directory
xx sh is a file
xx.sh is a file
$
Given that you are having problems, you should debug the script by adding:
ls -ld "${PASSED}"
This will show you what ls thinks about the names you pass the script.
Using -f and -d switches on /bin/test:
F_NAME="${1}"
if test -f "${F_NAME}"
then
echo "${F_NAME} is a file"
elif test -d "${F_NAME}"
then
echo "${F_NAME} is a directory"
else
echo "${F_NAME} is not valid"
fi
Using the "file" command may be useful for this:
#!/bin/bash
check_file(){
if [ -z "${1}" ] ;then
echo "Please input something"
return;
fi
f="${1}"
result="$(file $f)"
if [[ $result == *"cannot open"* ]] ;then
echo "NO FILE FOUND ($result) ";
elif [[ $result == *"directory"* ]] ;then
echo "DIRECTORY FOUND ($result) ";
else
echo "FILE FOUND ($result) ";
fi
}
check_file "${1}"
Output examples :
$ ./f.bash login
DIRECTORY FOUND (login: directory)
$ ./f.bash ldasdas
NO FILE FOUND (ldasdas: cannot open `ldasdas' (No such file or directory))
$ ./f.bash evil.php
FILE FOUND (evil.php: PHP script, ASCII text)
FYI: the answers above work but you can use -s to help in weird situations by checking for a valid file first:
#!/bin/bash
check_file(){
local file="${1}"
[[ -s "${file}" ]] || { echo "is not valid"; return; }
[[ -d "${file}" ]] && { echo "is a directory"; return; }
[[ -f "${file}" ]] && { echo "is a file"; return; }
}
check_file ${1}
Using stat
function delete_dir () {
type="$(stat --printf=%F "$1")"
if [ $? -ne 0 ]; then
echo "$1 directory does not exist. Nothing to delete."
elif [ "$type" == "regular file" ]; then
echo "$1 is a file, not a directory."
exit 1
elif [ "$type" == "directory" ]; then
echo "Deleting $1 directory."
rm -r "$1"
fi
}
function delete_file () {
type="$(stat --printf=%F "$1")"
if [ $? -ne 0 ]; then
echo "$1 file does not exist. Nothing to delete."
elif [ "$type" == "directory" ]; then
echo "$1 is a regular file, not a directory."
exit 1
elif [ "$type" == "regular file" ]; then
echo "Deleting $1 regular file."
rm "$1"
fi
}
https://linux.die.net/man/2/stat
https://en.m.wikipedia.org/wiki/Unix_file_types
A more elegant solution
echo "Enter the file name"
read x
if [ -f $x ]
then
echo "This is a regular file"
else
echo "This is a directory"
fi
Answer based on the title:
Check if passed argument is file or directory in Bash
This works also if the provided argument has a trailing slash .e.g. dirname/
die() { echo $* 1>&2; exit 1; }
# This is to remove the the slash at the end: dirName/ -> dirName
fileOrDir=$(basename "$1")
( [ -d "$fileOrDir" ] || [ -f "$fileOrDir" ] ) && die "file or directory $fileOrDir already exists"
Testing:
mkdir mydir
touch myfile
command dirName
# file or directory mydir already exists
command dirName/
# file or directory mydir already exists
command filename
# file or directory myfile already exists
#!/bin/bash
echo "Please Enter a file name :"
read filename
if test -f $filename
then
echo "this is a file"
else
echo "this is not a file"
fi
One liner
touch bob; test -d bob && echo 'dir' || (test -f bob && echo 'file')
result is true (0)(dir) or true (0)(file) or false (1)(neither)
This should work:
#!/bin/bash
echo "Enter your Path:"
read a
if [[ -d $a ]]; then
echo "$a is a Dir"
elif [[ -f $a ]]; then
echo "$a is the File"
else
echo "Invalid path"
fi

Resources