Bash Read Input - Tab the Prompt - bash

I've got a script that I'm reading the input from users. Here's my code:
if [ -z $volreadexists ]; then
echo -e "\tThis will overwrite the entire volume (/dev/vg01/$myhost)...are you sure?"
read REPLY
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo -e "\t\tContinuing"
syncvolume
else
echo "Fine...skipping"
fi
fi
I'm having to use read REPLY because read by itself doesn't insert tabs. What I'm looking for is something similar to:
read -p "\tDoes this look OK? (n for No)" -n 1 -r
Where \t would tab over the read prompt.
How can I add tabs to a read prompt?
UPDATE: Thanks for the great answer from #gniourf!:
read -p $'\tDoes this look OK? (n for No)' -n 1 -r
However, I found an issue. When I attempt to use a variable there it doesn't translate it:
read -p $'\tThis will overwrite the entire volume (/dev/vg01/$myhost)...are you sure? ' -n 1 -r
becomes
This will overwrite the entire volume (/dev/vg01/$myhost)...are you sure?
where I want:
This will overwrite the entire volume (/dev/vg01/server1)...are you sure?
Using doublequotes doesn't work either :(
Any ideas?

Just use ANSI-C quoting:
read -p $'\tDoes this look OK? (n for No)' -n 1 -r
Now, if you want to use variable expansions too, you can mix different quotes like so:
read -p $'\t'"This will overwrite the entire volume (/dev/vg01/$myhost)...are you sure? " -n 1 -r
Here I only used ANSI-C quoting for the tab character. Make sure you don't leave any spaces between $'\t' and "This will....".

I ended up referencing this answer:
Read a variable in bash with a default value
and created a workaround. It's not perfect, but it works:
myhost="server1"
if [ -z $volreadexists ]; then
read -e -i "$myhost" -p $'\tJust checking if it\'s OK to overwrite volume at /dev/vg01/'
echo
if [[ $REPLY =~ ^$myhost[Yy]$ ]]; then
echo -e "\t\tContinuing"
else
echo "Fine...skipping"
fi
fi

Related

How to use rm interactively in a while read loop?

I'm little stuck in a script that's used to find and delete certain files. I want to give the user the opportunity to iterate through the list and delete each file only after human approval.
However, I find the script to skip the user interaction and don't delete.
cat $fileToBeDeleted | while read in; do
rm -i "$in"
echo "deleted: $in"
done;
Instead of using rm -i you can use if statement to ask for confirmation before you delete the file. In this example I used a text file (to_delete_list.txt) that contains the list of files that I will be deleting.
read -u 1 answer will let you ask for user input in the loop.
#!/bin/bash
while IFS= read -r file; do
echo "do you want to delete $file (y/n)?"
read -u 1 answer
if [[ $answer = y ]]
then
rm $file
echo "deleted: $file"
else
continue
fi
done < "to_delete_list.txt"

how to check if a file exist and is a text file?

Hi everyone I need to check if a file exist with a shell script. I did some digging and ended up with this syntax but I'm not sure why it isn't working
(please bear in mind that you are talking to beginner)
I've found that you can add -e for example to check if it exist but I didn't get where these shortcuts came form or their names
#! /bin/bash
if [ "$#" = "1" ]
then
if [ -e $($1) ] && [ -f $($1) ]
then echo 'the file exists'
fi
fi
In idiomatic Bash:
#!/usr/bin/env bash
if [[ -f "${1-}" ]]
then
echo 'the file exists'
fi
Correct shebang
[[ rather than [
-f implies -e
No need for semicolons or single-use variables.
Please keep in mind that this does not tell you whether the file is a text file. The only "definition" of a text file as opposed to any other file is whether it contains only printable characters, and even that falls short of dealing with UTF BOM characters and non-ASCII character sets. For that you may want to look at the non-authoritative output of file "${1-}", for example:
$ file ~/.bashrc
/home/username/.bashrc: ASCII text
More in the Bash Guide.
#!/bin/bash
if [ "$#" == 1 ]; then
if [[ -e "$1" && -f "$1" ]]; then
echo 'The file exists';
fi
fi
You should put every conditional && between [[ ]] symbols otherwise it will be interpreted as execute if success.
#! /bin/sh
FILE=$1 # get filename from commandline
if [ -f $FILE ]; then
echo "file $FILE exists"
fi
See the fine manual page of test commands, which are built-in in the different shells: man test; man sh; man bash
You will also find many shell primers which explain this very nicely.
Or see bash reference manual: https://www.gnu.org/software/bash/manual/bash.pdf

Cannot pass variable into while loop

I've tried this a few ways but trying to pass a variable into this code when it's a text file just doesn't work. What's weird though is if the check sees it's just a url, it works perfectly.
I've tried -i in wget, quotes around $line, {} around line, putting $directory a few ways into wget. Nothing. It either reads it as blank or as the file name, not the urls in the file.
On top of this mess, $savefile.log in the first part of the loop always returns directory.txt.log. Tried $line.log to fix that and nada. I do need it stripped as : and \ are not valid in a file name.
#!/bin/bash
read -p "Enter directory or .txt file: `echo $'\n> '`" directory
savefile=$(echo "${directory//"http://"}" | cut -d '/' -f1)
if [[ $directory == *.txt ]]
echo
echo "Spidering $directory"
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "$line"
wget -np --spider -e robots=off --no-check-certificate $line 2>> $savefile.log
echo
echo "Spider saved to $savefile.log"
done < $directory
else
echo
echo "Spidering $directory"
wget -r -np --spider -e robots=off --no-check-certificate $directory 2>> $savefile.log
echo
echo "Spider saved to $savefile.log"
fi
EDITx2??:
Removing my old answer because it was wrong, finally got a chance to sit down and run the code, and based off what I THINK you were looking for this should do it:
#!/bin/bash
read -p "Enter directory or .txt file: `echo $'\n> '`" directory
if [[ $directory == *.txt ]]; then
echo
echo "Spidering $directory"
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "$line"
savefile="$(basename "${directory//.txt}")"
wget -np --spider -e robots=off --no-check-certificate $line 2>> $savefile.log
echo
echo "Spider saved to $savefile.log"
done < $directory
else
echo
echo "Spidering $directory"
savefile=$(echo "${directory//"http://"}" | cut -d '/' -f1)
wget -r -np --spider -e robots=off --no-check-certificate $directory 2>> $savefile.log
echo
echo "Spider saved to $savefile.log"
fi
One big issue was you forgot the ; then at the end of the if [[ ... ]] line.
With savefile, I am guessing you were aiming to strip off extra after the ".com" on a url and give just the file name of a file? Works perfect for a url as written, but it nukes file path. Moved it into the else as written. For a file, basename removes the directories and just leaves the file name and the variable expansion strips the troublesome '.txt'.
Tried not to deviate from your code by much, but I would recommend quoting the variables - shouldn't be spaces in the URL, but ~could be~ if input wrong, but mainly because filenames could have un-escaped spaces.
Could also make a tad more compact by using 'echo -e "\nSpidering $directory"' instead of the double echoes. Don't suppose anything wrong with them but they bug my ocd. :P
Finally, I would recommend using the newer syntax for the command substitution, "$(echo "cmd")" instead of the backticks. Again not technically wrong, but since the back-ticks are deprecated they may eventually stop working. Also, and more so I'd say, it makes the code allot more readable, I have to squint at times to tell if it is a single quote or a back-tick.

Bash + check for file exist with a path home ~

I haven't found anything to deal with this particular situation. Maybe there is a easy way that I'm overlooking instead of checking for a string to catch this scenario. When I check an input for existence of a file, if the input is ~/filecheck , this won't work. I get negative results while the file is in my home folder. Any suggestions for improvement to any part of the script I will definitely appreciate. I also have to use an input instead of a argument. Thanks for any help.
my test script
read -p "Enter: " input
echo $input
if [ -f $input ]; then
read -p "Do you REALLY want to delete this file?:" input2
if [[ $input2='y' || $input2 = 'Y' ]]
then
rm -f $input
elif [[ $input2='n' || $input2='N' ]]
then
exit
else
echo "Invaild Option"
exit
fi
else
echo Invaild Option!
exit
fi
Since you are entering input string as ~/filecheck shell doesn't expand tilde while using condition with -f in [ -f $input ]
You can use it this way but it is not recommended and potentially dangerous as arbitrary commands can be run by user:
if [[ -f $(bash -c "echo $input") ]]; then
echo "file exists"
fi
EDIT: As per the comments below to avoid risky bash -c you can use:
if [[ -f "${input/\~/$HOME}" ]]; then
echo "file exists"
fi
You can't have tilde expansion in this part of the program without using something based on eval—and you don't want to do that with user input. So, your poor-man solution will be to substitute any potential leading ~/ with the expansion of $HOME/. Here's the adaptation of your script in an arguably better style:
#!/bin/bash
read -e -p "Enter: " input
input=${input/#~\//$HOME/} # <--- this is the main idea of this answer (and it's rather poor)
echo "$input"
if [[ -f $input ]]; then
read -e -p "Do you REALLY want to delete this file? " input2
if [[ ${input2,,} = y ]]; then
rm -f -- "$input"
elif [[ ${input2,,} = n ]]; then
exit
else
echo "Invalid Option"
exit
fi
else
echo "Invalid Option!"
fi
exit
Now, out of curiosity, why are you spending time to make a wrapper around rm? you're making a clunky interface to an already existing program, without adding anything to it, only rendering it less powerful and less easy to use.
If all what you want it's to ask the user before deleting, you can use:
rm -i
This will give you appropriate error in the case file does not exist.

How to change parameter in a file, only if the file exists and the parameter is not already set?

#!/bin/bash
# See if registry is set to expire updates
filename=hostnames
> test.log
PARAMETER=Updates
FILE=/etc/.properties
CODE=sudo if [ ! -f $FILE] && grep $PARAMETER $FILE; then echo "File found, parameter not found."
#CODE=grep $PARAMETER $FILE || sudo tee -a /etc/.properties <<< $PARAMETER
while read -r -a line
do
hostname=${line//\"}
echo $hostname":" >> test.log
#ssh -n -t -t $hostname "$CODE" >> test.log
echo $CODE;
done < "$filename"
exit
I want to set "Updates 30" in /etc/.properties on about 50 servers if:
The file exists (not all servers have the software installed)
The parameter "Updates" is not already set in the file (e.g. in case of multiple runs)
I am a little puzzled so far how, because I am not sure if this can be done in 1 line of bash code. The rest of the script works fine.
Ok, here's what i think would be a solution for you. Like explained in this article http://www.unix.com/shell-programming-scripting/181221-bash-script-execute-command-remote-servers-using-ssh.html
invoke the script which contains the commands that you want to be executed at the remote server
Code script 1:
while read -r -a line
do
ssh ${line} "bash -s" < script2
done < "$filename"
To replace a line in a text file, you can use sed (http://www.cyberciti.biz/faq/unix-linux-replace-string-words-in-many-files/)
Code script 2:
PARAMETER=Updates
FILE=/etc/.properties
NEWPARAMETER=Updates ###(What you want to write there)
if [ ! -f $FILE] && grep $PARAMETER $FILE; then exit
sed -i 's/$PARAMETER/$NEWPARAMETER/g' $FILE
So, I'm not certain this covers all your use case, I hope this helps you out if there is anything feel free to ask!

Resources