Deleting a line that contains a string - shell

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.

Related

how can I loop with an error message?

Latest Version
#!/bin/bash
set -e
shopt -s nocasematch
#vars
redbgbold='\e[1;97;41m'
resetcolor='\e[0m'
RegExFQDN='(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)'
#functions
ask() {
local input
until
read -rp "$1 > " input >&2 || return 1
grep -q -P "$2" <<< "$input"
do
printf "ERROR - "${redbgbold}"\"$input\""${resetcolor}" is not a valid " >&2; sed "s/.*the //" <<< "$1" >&2
done
printf '%s\n' "$input"
}
#code
while [ -z $fqdn ]; do
fqdn=$(ask "Enter the FQDN" $RegExFQDN)
echo "FQDN is $fqdn"
done
The Question
I have a read line, and I want to take what the user entered and see if it matches my regex, if it matches we leave the loop, if it fails it prints an error and we do the loop again until we get a match. It looks redundant to me, and I assume there should be a better way but not sure what that should be.
Original Code
#!/bin/bash
set -e
shopt -s nocasematch
function RegexValidation() {
if [ "$2" = "fqdn" ]; then
if [ `echo $1 | grep -c -P '(?=^.{1,254}$)(^(?>(?!\d+\.)[a-z0-9_\-]{1,63}\.?)+(?:[a-z]{2,})$)'` == "0" ]; then
echo "ERROR - $1 is not a valid FQDN"
unset $!{1}
fi
fi
}
while [ -z $fqdn ]; do
read -e -r -p "Enter the Fully Qualified Domain Name > " fqdn
RegexValidation $fqdn fqdn
done
shopt -u nocasematch
any help is appreciated.
Update #1 - fixed formatting issues.
Update #2 - using that other guy's suggestions with a few additional tweaks
I would do basically the same thing, but split it differently to make it easier to reuse:
#!/bin/bash
set -e
ask() {
local input
until
read -rp "$1 > " input >&2 || return 1
grep -q -P "$2" <<< "$input"
do
echo "Invalid answer. Try again" >&2
done
printf '%s\n' "$input"
}
ask_fqdn() {
ask "$1" '(?=^.{1,254}$)(^(?>(?!\d+\.)[a-z0-9_\-]{1,63}\.?)+(?:[a-z]{2,})$)'
}
fqdn=$(ask_fqdn "Enter first FQDN")
echo "You wrote $fqdn"
fqdn=$(ask_fqdn "Enter second FQDN")
echo "This time it was $fqdn"
number=$(ask "And now a number because why not" '^\d+$')
echo "So $number"
Now you don't have to write a new loop every time you want new information, and you can easily ask for new things without modifying the existing functions.
Have the function return a status, which you can test with if in the loop.
And rather than use test to check the result of grep, just test it directly with if. grep returns a non-zero status if the input doesn't match.
function RegexValidation() {
if [ "$2" = "fqdn" ]; then
if ! echo "$1" | grep -q -P '(?=^.{1,254}$)(^(?>(?!\d+\.)[a-z0-9_\-]{1,63}\.?)+(?:[a-z]{2,})$)'; then
echo "ERROR - $1 is not a valid FQDN"
return 1
fi
return 0
fi
}
while :; do
read -e -r -p "Enter the Fully Qualified Domain Name > " fqdn
if RegexValidation "$fqdn" fqdn
then break
fi
done
Also, remember to quote your variables.

unary operator expected with more than 1 argument

for var in "$#"
do
if test -z $var
then
echo "missing operand"
elif [ -d $var ]
then
echo "This is a directory"
elif [ ! -f $var ]
then
echo "The file does not exist"
else
basename=$(basename $var)
dirname=$(readlink -f $var)
inodeno=$(ls -i $var| cut -d" " -f1)
read -p "remove regular file $#" input
if [ $input = "n" ]
then exit 1
fi
mv $var "$var"_"$inodeno"
echo "$basename"_"$inodeno":"$dirname" >> $HOME/.restore.info
mv "$var"_"$inodeno" $HOME/deleted
fi
done
**Hello, the above code is trying to mimic the rm command in unix. Its purpose is to remove the file .
Eg if I type in bash safe_rm file1 , it works however if type in
bash safe_rm file1 file 2 , it prompts me to remove file 1 twice and gives me a unary operater expected for line 27(if [ $input = "n" ]).
Why does it not work for two files, ideally I would like it to prompt me to remove file1 and file 2.
Thanks
read -p "remove regular file $#" input
should probably be
read -p "remove regular file $var" input
That's the basic.
And this is how I'd prefer to do it:
for T in "$#"; do
if [[ -z $T ]]; then
echo "Target is null."
elif [[ ! -e $T ]]; then
echo "Target does not exist: $T"
elif [[ -d $T ]]; then
echo "Target can't be a directory: $T"
else
BASE=${T##*/}
DIRNAME=$(exec dirname "$T") ## Could be simpler but not sure how you want to use it.
INODE_NUM=$(exec stat -c '%i' "$T")
read -p "Remove regular file $T? "
if [[ $REPLY == [yY] ]]; then
# Just copied. Not sure about its logic.
mv "$T" "${T}_${INODE_NUM}"
echo "${BASE}_${INODE_NUM}:${DIRNAME}" >> "$HOME/.restore.info"
mv "${T}_${INODE_NUM}" "$HOME/deleted"
fi
fi
done

Need help on ssh usage in a loop of shell script

I need help on how to connect remote systems through ssh command in while loop of shell script. I was able to connect one remote system using ssh from shell script. Please find sample code snippet as given below…..
ssh "root#148.147.179.100" ARG1=$rpmFileName 'bash -s' <<'ENDSSH'
echo ">>Checksum ..."
md5sum /root/$ARG1
ENDSSH
When tried to run same piece of code within a loop getting the error "syntax error: unexpected end of file", which I couldn’t resolve.
But when placing the same piece of code in another script file and using that file in while loop of another script, is working.
Can anyone help me with some solution.
Please find entire code as given below...
#!/bin/sh
rpmFileName=""
file="serverIps.txt"
dir="/home/rtulluri/downloads/EVAT-1123/AxisTar";
numberOfIps=0
axisTarfileTarget='/var'
#This function checks whether given ip is valid or not
#returns 0 for valid ip, 1 for invalid ip
function valid_ip()
{
local ip=$1
local stat=1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
#echo "stat = $stat"
return $stat
}
#Check whether given file exists or not
if [ -s $file ]
then
echo "$file exists"
else
echo "$file doesn't exist"
echo "exiting ........"
exit
fi
IFS=,
echo "---------------"
while read sysType serverIp uid pwd
do
sysType="${sysType#"${sysType%%[![:space:]]*}"}" # remove leading whitespace characters
sysType="${sysType%"${sysType##*[![:space:]]}"}" # remove trailing whitespace characters
serverIp="${serverIp#"${serverIp%%[![:space:]]*}"}" # remove leading whitespace characters
serverIp="${serverIp%"${serverIp##*[![:space:]]}"}" # remove trailing whitespace characters
uid="${uid#"${uid%%[![:space:]]*}"}" # remove leading whitespace characters
uid="${uid%"${uid##*[![:space:]]}"}" # remove trailing whitespace characters
pwd="${pwd#"${pwd%%[![:space:]]*}"}" # remove leading whitespace characters
pwd="${pwd%"${pwd##*[![:space:]]}"}" # remove trailing whitespace characters
if [ -n "$serverIp" ]
then
valid_ip $serverIp
#Assign the return value to a variable
isValidIp=$?
else
isValidIp=1
fi
if [ $isValidIp -eq "0" ]
then
numberOfIps=$(( $numberOfIps + 1 ))
echo "$numberOfIps) $serverIp --> is valid"
if [ "$sysType" = "ebox" ]
then
echo "$serverIp is an eBox device.."
echo "About to pass $serverIp as argument to connct.sh"
#./connct.sh $serverIp
ssh "$address" ARG1=$rpmFileName 'bash -s' <<'ENDSSH'
echo ">>Checksum ..."
ENDSSH
fi
else
echo "$serverIp --> is invalid"
fi
echo ""
done < $file
You might try to use the '-n' ssh switch
e.g.:
for i in {1..7}
do
ssh -n myhost$i mycommand
done

Bash, always echo in conditional statement

This may turn out to be more of a thought exercise, but I am trying to echo a newline after some command I'm executing within a conditional. For example, I have:
if ssh me#host [ -e $filename ] ; then
echo "File exists remotely"
else
echo "Does not exist remotely"
fi
And want to throw in an echo after the ssh command regardless of the outcome. The reason is formatting; that way a newline will exist after the prompt for password for ssh.
First Try
if ssh me#host [ -e $filename ] && echo ; then
Because && echo would not change the conditional outcome, but bash would not execute echo if ssh returned false. Similarly,
if ssh me#host [ -e $filename ] || (echo && false) ; then
Does not work because it will short-circuit if ssh returns true.
An answer to the problem would be
ssh me#host [ -e $filename ]
result=$?
echo
if [ $result == 0 ] ; then
but was wondering if there was some similar conditional expression to do this.
Thanks.
While this would work
if foo && echo || ! echo; then
I'd prefer putting the whole thing into a function
function addecho() {
"$#" # execute command passed as arguments (including parameters)
result= $? # store return value
echo
return $result # return stored result
}
if addecho foo; then
What about this?
if ssh me#host [ -e $filename ] && echo || echo; then
I have not thought about precedence order of && and || and surely putting some parenthesis would help, but like that it works already... you get the echo both when ssh fails and when it succeeds...
Add the "echo" before the filename test
if ssh me#host "echo; [ -e $filename ]"; then
echo "File exists remotely"
else
echo "Does not exist remotely"
fi

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