Syntax error near unexpected token `done' - Shell Scripting - shell

#!/bin/sh
VAR2=0
while [ $VAR2 -eq 0 ]: do
echo "Please choose one of the following options:"
echo "1. List the current running processes"
echo "2. Check the available free memory"
echo "3. List the disks/partitions"
echo "4. Check for hardware (PCI)"
echo "5. Check for package installation"
echo "6. Create multiple files"
echo "7. Remove multiple files"
echo "8. List the contents of the current directory"
echo "0. Exit"
read VAR1
if [ $VAR1 -eq 0 ]; then
VAR2=2
fi
if [ $VAR1 -eq 1 ]; then
$(top)
fi
if [ $VAR1 -eq 2 ]; then
$(free)
fi
if [ $VAR1 -eq 3 ]; then
$(df)
fi
if [ $VAR1 -eq 4 ]; then
echo "Insert the name of the hardware that you want to search:" read VARHARD $(sudo lspci | grep $VARHARD)
fi
if [ $VAR1 -eq 5 ]; then
echo "Insert the name of the package that you want to search:" read VARPACK $(rpm -qa | grep VARPACK)
fi
if [ $VAR1 -eq 6 ]; then
echo "Insert the base name of the files:" read VARFILE echo "Insert the amount of files you want:" read VARNUMB $(touch $VARFILE{0001..000$VARNUMB})
fi
if [ $VAR1 -eq 7 ]; then
echo "Insert a string to delete all files that contain it:" read VARDEL $(find -type f -name '*$VARDEL*' -exec rm {} \;)
fi
if [ $VAR1 -eq 8 ]; then
$(ls -la)
fi
echo "Press any key and enter to continue... "
read teste
done
So, when I try to run the script "sh script.sh", it gives me an error that says "Syntax error near unexpected token `token'"
Can someone explain the error to me please? I'm new on scripting.
Thanks!

You have two problems in your code, the first is the invocation of subshells where is not due (using $() ) and the second is a typo at the line 26 (you have if instead of fi). The following corrected code works:
#!/bin/bash
VAR2=0
while [ $VAR2 -eq 0 ]; do
echo "Please choose one of the following options:"
echo "1. List the current running processes"
echo "2. Check the available free memory"
echo "3. List the disks/partitions"
echo "4. Check for hardware (PCI)"
echo "5. Check for package installation"
echo "6. Create multiple files"
echo "7. Remove multiple files"
echo "8. List the contents of the current directory"
echo "0. Exit"
read VAR1
if [ $VAR1 -eq 0 ]; then
VAR2=2
fi
if [ $VAR1 -eq 1 ]; then
top
fi
if [ $VAR1 -eq 2 ]; then
free
fi
if [ $VAR1 -eq 3 ]; then
df
fi
if [ $VAR1 -eq 4 ]; then
echo "Insert the name of the hardware that you want to search:"
read VARHARD
sudo lspci | grep $VARHARD
fi
if [ $VAR1 -eq 5 ]; then
echo "Insert the name of the package that you want to search:"
read VARPACK
rpm -qa | grep VARPACK
fi
if [ $VAR1 -eq 6 ]; then
echo "Insert the base name of the files:"
read VARFILE
echo "Insert the amount of files you want:"
read VARNUMB
touch $VARFILE{0001..000$VARNUMB
fi
if [ $VAR1 -eq 7 ]; then
echo "Insert a string to delete all files that contain it:"
read VARDEL
find -type f -name '*$VARDEL*' -exec rm {} \;
fi
if [ $VAR1 -eq 8 ]; then
ls -la
fi
echo "Press any key and enter to continue... "
read teste
done

Why are you using the syntax $(top)? That will execute top to completion (it may be long running, and never end), and then evaluate the output as a command and attempt to execute it. Most likely, the output of top is not valid shell syntax. I'm not sure exactly which command is generating the syntax error related to token, but that's probably the source of your error. Instead of $(top), just write top. Same for all the other instances of $() in the script.

Related

How to Ask User for Confirmation: Shell

I am new to shell, and my code takes two arguments from the user. I would like to confirm their arguments before running the rest of the code. I would like a y for yes to prompt the code, and if they type n for no, then the code will ask again for new arguments
Pretty much, if i type anything when I am asked to confirm, the rest of the code runs anyways. I tried inserting the rest of the code after the first then statement, but that didn't work either. I have also checked my code with ShellCheck and it all appears to be legal syntax. Any advice?
#!/bin/bash
#user passes two arguments
echo "Enter source file name, and the number of copies: "
read -p "Your file name is $1 and the number of copies is $2. Press Y for yes N for no " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
echo "cloning files...."
fi
#----------------------------------------REST OF CODE
DIR="."
function list_files()
{
if ! test -d "$1"
then echo "$1"; return;
fi
cd ... || $1
echo; echo "$(pwd)":; #Display Directory name
for i in *
do
if test -d "$i" #if dictionary
then
list_files "$i" #recursively list files
cd ..
else
echo "$i"; #Display File name
fi
done
}
if [ $# -eq 0 ]
then list_files .
exit 0
fi
for i in "$#*"
do
DIR=$1
list_files "$DIR"
shift 1 #To read next directory/file name
done
if [ ! -f "$1" ]
then
echo "File $1 does not exist"
exit 1
fi
for ((i=0; i<$2; i++))
do
cp "$1" "$1$i.txt"; #copies the file i amount of times, and creates new files with names that increment by 1
done
status=$?
if [ "$status" -eq 0 ]
then
echo 'File copied succeaful'
else
echo 'Problem copying'
fi
Moving the prompts into a while loop might help here. The loop will re-prompt for the values until the user confirms them. Upon confirmation, the target code will be executed and the break statement will terminate the loop.
while :
do
echo "Enter source file name:"
read source_file
echo "Number of copies"
read number_of_copies
echo "Your file name is $source_file and the number of copies is $number_of_copies."
read -p "Press Y for yes N for no " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "cloning files...."
break ### <<<---- terminate the loop
fi
echo ""
done
#----------------------------------------REST OF CODE

i don't know if my shell script is correct

I have a homework using for loop but I'm not quite understand the task that I have to do in there. I wrote a script but I feel like it's not a correct script. Please help!
Here is the question:
Write a shell script to list out the contents of any directory, and indicate for each file (including invisible ones) whether the file is a directory, a plain file, and whether it is public and/or executable to this process
#!/bin/bash
if [ $# -lt 1 ] ; then
echo " file doesn't exist"
echo
echo " variable needed to run a command"
fi
echo ---------------------------------------------
echo ---------------------------------------------
for i in $*
do
if [ -f $i ]; then
echo " it's a file";
echo "THIS IS A LIST OF FILE and DIRECTORY in $i"
ls -a $i
fi
done
echo -----------------------------------------
if [ -d $i ]; then
echo "directory" ;
echo "THIS IS A LIST OF FILES AND DIRETORY in $i"
ls -a $i
fi
echo ------------------------------------------
if [ -x $i ]; then
echo "executable"
echo "THIS IS A LIST OF EXECUTABLE FILE IN $i"
ls -x $i
fi
echo -----------------------------------------
if [ -r $i ]; then
echo "this file is a public file"
else "this is a private file"
fi
#!/bin/bash
if [ $# -lt 1 ] ; then
echo " file doesn't exist"
echo
echo " variable needed to run a command"
fi
echo ---------------------------------------------
echo ---------------------------------------------
for i in $*
do
if [ -f $i ]; then
echo " it's a file";
echo "THIS IS A LIST OF FILE and DIRECTORY in $i"
ls -a $i
fi
done
echo -----------------------------------------
if [ -d $i ]; then
echo "directory" ;
echo "THIS IS A LIST OF FILES AND DIRETORY in $i"
ls -a $i
fi
echo ------------------------------------------
if [ -x $i ]; then
echo "executable"
echo "THIS IS A LIST OF EXECUTABLE FILE IN $i"
ls -x $i
fi
echo -----------------------------------------
if [ -r $i ]; then
echo "this file is a public file"
else "this is a private file"
fi
Poorly written specifications are the bane of education. "Public" sounds like the wrong word here. I'll assume it means "readable".
You check if there's an argument, but you don't exit the program if there is not. I'd also confirm it's a directory, and readable.
The manual will do you a lot of good. Expect to do a lot of reading till you learn this stuff, and then reference it a lot to be sure.
Read this section carefully, create some tests for yourself to prove they work and that you understand them, and your job will be more than half done.
Don't use [. Generally it's just better to always use [[ instead, unless you are using (( or case or some other construct.
I don't see that a for loop was specified, but it ought to be fine. Just be aware that you might have to specify $1/* and $1/.* separately.
Put all your tests in one loop, though. For each file, test for whether it's a directory - if it is, report it. Test if it's a plain file - if it is, report it.
I do NOT like doing homework for someone, but it looks like you could use an example that simplifies this. I recommend you not use this as written - break it out and make it clearer, but this is a template for the general logic.
#! /bin/env bash
(( $# )) && [[ -d "$1" ]] && [[ -r "$1" ]] || {
echo "use: $0 <dir>" >&2
exit 1
}
for e in "$1"/.* "$1"/*
do echo "$e:"
[[ -d "$e" ]] && echo " is a directory"
[[ -f "$e" ]] && echo " is a plain file"
[[ -r "$e" ]] && echo " is readable"
[[ -x "$e" ]] && echo " is executable"
done
If you read the links I provided you should be able to break this apart and understand it.
Generally, your script is long and a bit convoluted. Simpler is easier to understand and maintain. For example, be very careful about block indentation to understand scope.
$: for i in 1 2 3
> do echo $i
> done
1
2
3
$: echo $i
3
Compare this to -
for i in $*
do if [ -f $i ]; then
echo " it's a file";
echo "THIS IS A LIST OF FILE and DIRECTORY in $i"
ls -a $i
fi
done
echo -----------------------------------------
if [ -d $i ]; then
echo "directory" ;
echo "THIS IS A LIST OF FILES AND DIRETORY in $i"
ls -a $i
fi
You are testing each entry to see if it is a file, and if it is, reporting "THIS IS A LIST OF FILE and DIRECTORY in $i" every time...
but then only testing the last one to see if it's a directory, because the [ -d $i ] is after the done.
...did you run this somewhere to try it, and look at the results?

sed command in Solaris server

I have many javascript files in my solaris server which have some debug, print and trace statements which I want to comment. There are hundreds of file like this.
I have found a script to do this but the problem is the script is removing the urhk_b2k_printRepos statement instead of commenting it. The script is as below:
if [ $# -ne 2 ]
then
echo "usage: prdel <script file name> <directory in which scripts are present>"
exit 1
fi
file=$1
dir=$2
if [ ! -s ${file} ]
then
echo "script file ${file} either does not exist or is empty (zero bytes)"
echo "Exiting..."
exit 1
fi
if [ ! -d ${dir} ]
then
echo "Invalid directory ${dir} entered"
echo "Exiting..."
exit 1
fi
cordir="./corrected"
prlogdir="./prlog"
if [ -d ${cordir} ]
then
echo "The corrected directory exist in the path, Please remove and run the tool again"
echo "Exiting..."
exit 1
else
mkdir ${cordir}
fi
if [ -d ${prlogdir} ]
then
echo "The prlog directory exist in the path, Please remove and run the tool again"
echo "Exiting..."
exit 1
else
mkdir ${prlogdir}
fi
errFile="$prlogdir/scr_err.rpt"
sucFile="$prlogdir/scr_suc.rpt"
Lines=`wc -l $file`
cntr=1
while [ $cntr -le $Lines ]
do
src=`head -$cntr $file|tail -1`
echo "$cntr. Processing $src...."
srcPath="${dir}/${src}"
if [ ! -s ${srcPath} ]
then
echo "Script file ${src} does not exist in the path given" >> ${errFile}
else
cp $srcPath $cordir/$src.tmp
srctemp="$cordir/$src.tmp"
srccor="$cordir/$src.corrected"
printcnt=`grep -ci "^[ ]*print" $srctemp`
if [ $printcnt -ne 0 ]
then
cat $srctemp|sed 's/^[ ]*[ ]*print[ ]*(/#print(/'|sed 's/^[ ]*[ ]*PRINT[ ]*(/#PRINT(/' > $srccor
mv $srccor $srctemp
fi
prreposcnt=`grep -ci "printrepos" $srctemp`
if [ $prreposcnt -ne 0 ]
then
cat $srctemp|sed 's/^.*urhk_b2k_printRepos.*/#Printrepos statement removed/'|sed 's/^.*urhk_B2k_PrintRepos.*/#Printrepos statement removed/'|sed 's/^.*urhk_B2k_printRepos.*/#Printrepos statement removed/'|sed 's/^.*urhk_b2k_PrintRepos.*/#Printrepos statement removed/' > $srccor
else
cp $srctemp $srccor
fi
echo "Script file $src correction is done" >> ${sucFile}
rm $srctemp
diff $srcPath $srccor >> $prlogdir/$src.diff.rpt
fi
cntr=`expr $cntr + 1`
done
echo "done"
I am completely new to shell scripting. Can anyone help me to modify this script to comment "urhk_b2k_printRepos" lines and also comment "TRACE ON" lines.

syntax error near unexpected token `}' `

I'm writing a shell script to make a new save file for the game risk of rain. I'm trying to make it so that each section of the unlocks that are available, such as achievements, monster logs, artifacts, etc. can either be added all at once to the file, or the user will be able to pick which ones they want added. I am new to writing shell scripts and I am not quite sure why I am getting the error syntax error near unexpected token `}' when I run what I have so far of my script. If anyone could explain to me as to why I am getting the error, how to fix it, and/or how to improve my script, it would be greatly appreciated. Also I am on Mac OS X if that matters. Here is my script.
#!/bin/bash
mkdir ~/Desktop/ror_save_unlock_all
echo "This script takes ~/Library/Application Support/com.riskofrain.riskofrain/Save.ini and backs it up to ~/Desktop/ror_save_unlock_all/originalSave. It generates a new Save.ini that is built to your specifications, unlocking all items and achievements or just the ones you choose, and replaces the existing Save.ini with the new one." >> ~/Desktop/ror_save_unlock_all/README.txt
mkdir ~/Desktop/ror_save_unlock_all/originalSave
cp ~/Library/Application\ Support/com.riskofrain.riskofrain/Save.ini ~/Desktop/ror_save_unlock_all/originalSave/
cd ~/Desktop/ror_save_unlock_all
echo -n 'How would you like the new Save.ini to be built? Press 1 for all unlocks or 2 to customize.'
read text
if [ $text = "1" ]
then
{
echo "[Achievement]" >> EOF1
count=1
while [ $count -lt 55 ]
do
echo "acheivement${count}=\"2\"" >> EOF2
count=`expr $count + 1`
done
echo "[Record]" >> EOF3
count=1
while [ $count -lt 31 ]
do
echo "mons${count}=\"1\"" >> EOF4
count=`expr $count + 1`
done
count=1
while [ $count -lt 110 ]
do
echo "item${count}=\"1\"" >> EOF5
count=`expr $count + 1`
done
count=1
while [ $count -lt 10 ]
do
echo "artifact${count}=\"1\"" >> EOF6
count=`expr $count + 1`
done
cat EOF1 EOF2 EOF3 EOF4 EOF5 EOF6 > Save.ini
rm EOF1 EOF2 EOF3 EOF4 EOF5 EOF6
cp -force ~/Desktop/ror_save_unlock_all/Save.ini ~/Library/Application\ Support/com.riskofrain.riskofrain/Save.ini
echo "Original Save.ini successfully overwritten."
exit
}
elif [ $text = "2" ]; then
{
echo "You selected customize. Now we will build a custom Save.ini"
echo -n "Press 1 if you want to unlock all achievements, press 2 to continue without unlocking all achievements."
read text
if [ $text = "1" ]; then
{
echo "[Achievement]" >> EOF1
count=1
while [ $count -lt 55 ]
do
echo "acheivement${count}=\"2\"" >> EOF2
count=`expr $count + 1`
done
echo "All achievements successfully unlocked."
echo -n "Press 1 to make the Save.ini and replace the existing one with it and then exit, or press 2 to customize it further."
read text
if [ $text = "1" ]; then
{
cat EOF1 EOF2 > Save.ini
rm EOF1 EOF2
cp -force ~/Desktop/ror_save_unlock_all/Save.ini ~/Library/Application\ Support/com.riskofrain.riskofrain/Save.ini
echo "Original Save.ini successfully overwritten."
exit
}
elif [ $text = "2" ] then;
{
echo -n "Press 1 to unlock all monster logs, or press 2 to continue without unlocking all monster logs."
read text
if [ $text = "1" ]; then
{
echo "[Record]" >> EOF3
count=1
while [ $count -lt 31 ]
do
echo "mons${count}=\"1\"" >> EOF4
count=`expr $count + 1`
done
echo "All achievements successfully unlocked."
echo -n "Press 1 to make the Save.ini and replace the existing one with it and then exit, or press 2 to customize it further."
read text
}
}
}
}
Your script has some error:
- Line 93: elif [ $text = "2" ] then; should change to elif [ $text = "2" ]; then
- If command in bash must have fi to close a condition. You can refer to http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-6.html
Hope this help
Before deciding to write a script, you should have a basic understanding of the language in which you're writing. Even the most cursory reading of the documentation or any how-to pages would have told you the correct syntax for an if statement.
Beyond that, you have a number of inefficiencies in your script. The backtick operator starts a subshell, a new instance of bash, so this is not necessarily something you want to be doing 110 times in a loop.
Here are a couple of your bigger problems fixed. I'd suggest looking into functions to eliminate a lot of code repetition and conditional nesting.
#!/bin/bash
mkdir ~/Desktop/ror_save_unlock_all
echo "This script takes ~/Library/Application Support/com.riskofrain.riskofrain/Save.ini and backs it up to ~/Desktop/ror_save_unlock_all/originalSave. It generates a new Save.ini that is built to your specifications, unlocking all items and achievements or just the ones you choose, and replaces the existing Save.ini with the new one." >> ~/Desktop/ror_save_unlock_all/README.txt
mkdir ~/Desktop/ror_save_unlock_all/originalSave
cp ~/Library/Application\ Support/com.riskofrain.riskofrain/Save.ini ~/Desktop/ror_save_unlock_all/originalSave/
cd ~/Desktop/ror_save_unlock_all
read -n 1 -p 'How would you like the new Save.ini to be built? Press 1 for all unlocks or 2 to customize.' text
if [ $text = "1" ]
then
echo "[Achievement]" > Save.ini
for count in {1..54}; do
echo "acheivement${count}=\"2\"" >> Save.ini
done
echo "[Record]" >> Save.ini
for count in {1..30}; do
echo "mons${count}=\"1\"" >> Save.ini
done
for count in {1..109}; do
echo "item${count}=\"1\"" >> Save.ini
done
for count in {1..9}; do
echo "artifact${count}=\"1\"" >> Save.ini
done
cp -force ~/Desktop/ror_save_unlock_all/Save.ini ~/Library/Application\ Support/com.riskofrain.riskofrain/Save.ini
echo "Original Save.ini successfully overwritten."
exit
elif [ $text = "2" ]; then
echo "You selected customize. Now we will build a custom Save.ini"
read -n 1 -p "Press 1 if you want to unlock all achievements, press 2 to continue without unlocking all achievements." text
if [ $text = "1" ]; then
echo "[Achievement]" > Save.ini
for count in {1..54}; do
echo "acheivement${count}=\"2\"" >> Save.ini
done
echo "All achievements successfully unlocked."
read -n 1 -p "Press 1 to make the Save.ini and replace the existing one with it and then exit, or press 2 to customize it further." text
if [ $text = "1" ]; then
cp -force ~/Desktop/ror_save_unlock_all/Save.ini ~/Library/Application\ Support/com.riskofrain.riskofrain/Save.ini
echo "Original Save.ini successfully overwritten."
exit
elif [ $text = "2" ]; then
read -n 1 -p "Press 1 to unlock all monster logs, or press 2 to continue without unlocking all monster logs." text
if [ $text = "1" ]; then
echo "[Record]" >> Save.ini
for count in {1..30}; do
echo "mons${count}=\"1\"" >> Save.ini
done
echo "All monster logs successfully unlocked."
read -n 1 -p "Press 1 to unlock all monster logs, or press 2 to continue without unlocking all monster logs." text
if [ $text = "1" ]; then
#...
fi
fi
fi
fi
fi

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

Resources