In Unix, WHILE command, I am trying to read a file, which is in another directory. But somehow not working, not even throwing any error.
while read line
do
echo $line
done < /tmp/myfile.txt
The file is present in /tmp folder, has all the permissions.
It is not clear why your while loop is not working. Normally loops, if condition syntax are different for different shell. Hence at the beginning of a shell script file we always define the shell where exactly this script should run and that first line is start with a #. Now do the following and check that might help you.
create a file $vi test.sh
put the below line in it
#!/usr/bin/ksh
filename="/tmp/myfile.txt"
while [ 1 ]
do
read -r line
if [ ${line:-1} -eq 1 ]; then
break
else
echo $line
fi
done < "$filename"
or
#!/usr/bin/ksh
filename="/tmp/myfile.txt"
while read -r line
do
echo $line
done < "$filename"
save the file and set the permission like below
$chmod 777 test.sh
now run the file
$./test.sh
I am in root directory, i am creating a script that will take me from root > Home > Logs and inside logs delete 3 log files.
Script will check if they exist, if YES it will delete it.
I am facing some syntax problems if you could help.
Thanks
My code:
#!/bin/sh
cd Home/Log
if [ -e error1.log ]
then
rm error1
fi
if [ -e error2.log ]
then
rm error1
fi
if [ -e error3.log ]
then
rm error1
fi
when i execute the file in root using ./delete here is what is am getting as errors:
$ ./delete
: No such file or directoryme/Log
./delete: line 14: syntax error near unexpected token `fi'
I am in root directory
When writing a script, it's almost always better not to assume things like that. If you know where the files are and it's not important that they're somewhere relative to what happens to be your current working directory, just name them.
Here are three ways you could accomplish what you want safely.
#!/bin/sh
dir=/Home/Log
rm -f ${dir}/error1.log ${dir}/error2.log ${dir}/error2.log
or
#!/bin/sh
dir=/Home/Log
rm -f ${dir}/error{1,2,3}.log
or
#!/bin/sh
set -e
cd /Home/Log && rm -f error1.log error2.log error2.log
For anything nontrivial, set -e is your friend. In your example, nothing happens later in the script. What you don't want is to keep going thinking you've changed directories, but haven't, and wind up scribbling somewhere you didn't intend. Many have lost much that way.
What's wrong with this code?
sudo -u replicant rsync -av -e "ssh -o 'StrictHostKeyChecking no' -i /home/replicant/.ssh/id_rsa" --exclude 'media/' --exclude 'var/' --exclude '.svn' root#$ADMIN:/var/www/ /var/www/ &> /tmp/rsync if [ $? -ne 0 ]; then
echo "date: Error rsync'ing code base from $ADMIN check /tmp/rsync" | mail -s "Rsync error!" $DEVEMAIL
echo "date: Error rsync'ing code base from $ADMIN check /tmp/rsync" >> $LOGFILE
echo "root#$ADMIN:/var/www /var/www" >> $LOGFILE
exit
fi
I keep getting this error:
Permission denied (publickey).
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: unexplained error (code 255) at io.c(605)
[Receiver=3.0.9]
Please help. Thanks.
Try to login directly on SSH to fix your issues, then move on to your rsync test. So start with:
ssh -o 'StrictHostKeyChecking no' -i /home/replicant/.ssh/id_rsa root#$ADMIN
Sidenotes:
don't use root for such a task
add set -eu at the start of your Bash script, so that errors will end up your script and ease debugging (for example if $ADMIN is not defined, the script will end in error)
I'm not sure why this code isn't working. Its not going to the copy command.
I successfully run this manually on the command line (without the check)
I don't think i'm performing a correct file check? Is there a better, cleaner way to write this?
I just want to make sure the file exists, if so, copy it over. Thanks.
#!/bin/bash
if [ $# != 1 ]; then
echo "Usage: getcnf.sh <remote-host>" 2>&1
exit 1
fi
#Declare variables
HOURDATE=`date '+%Y%m%d%H%M'`
STAMP=`date '+%Y%m%d-%H:%M'`
REMOTE_MYCNF=/var/log/mysoft/mysoft.log
BACKUP_DIR=/home/mysql/dev/logs/
export REMOTE_MYCNF HOURDATE STAMP
#Copy file over
echo "Checking for mysoft.log file $REMOTE_MYCNF $STAMP" 2>&1
if [ -f $REMOTE_MYCNF ]; then
echo "File exists lets bring a copy over...." 2>&1
/usr/bin/scp $1:$REMOTE_MYCNF $BACKUP_DIR$1.mysoft.log
echo "END CP" 2>&1
exit 0
else
echo "Unable to get file" 2>&1
exit 0
fi
your checking existing file on remote computer seems like:
you should do:
ssh $host "test -f $file"
if [ $? = 0 ]; then
use sh -x script.sh to see what is happening.
You are testing for the existence of a remote file
$1:$REMOTE_MYCNF
using the local name $REMOTE_MYCNF. The if test is never satisfied.
You don't check that $1 is set.
Your file check runs on the local machine - not on the remote.
Change your if to:
if[! -f $REMOTE_MYCNF -o ! -d $REMOTE_MYCNF];
I have a cron job:
$SP_s/StartDailyS1.sh >$LP_s/MirrorLogS1.txt
Where SP_s is the path to the script and LP_s is the path for the log file. This sends stdout to the log file and stderr to my email.
How do I?:
1) send both stdout AND stderr to the logfile,
2) AND send stderr to email
or to put it another way: stderr to both the logfile and the email, and stdout only to the logfile.
UPDATE:
None of the answers I've gotten so far either follow the criteria I set out or seem suited to a CRON job.
I saw this, which is intended to "send the STDOUT and STDERR from a command to one file, and then just STDERR to another file" (posted by zazzybob on unix.com), which seems close to what I want to do and I was wondering if it would inspire someone more clever than I:
(( my_command 3>&1 1>&2 2>&3 ) | tee error_only.log ) > all.log 2>&1
I want cron to send STDERR to email rather than 'another file'.
Not sure why nobody mentioned this.
With CRON if you specify MAILTO= in the users crontab,
STDOUT is already sent via mail.
Example
[temp]$ sudo crontab -u user1 -l
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=user1
# transfer order shipping file every 3 minutes past the quarter hour
3,19,33,48 * * * * /home/user1/.bin/trans.sh
Since I was just looking at the info page for tee (trying to figure out how to do the same thing), I can answer the last bit of this for you.
This is most of the way there:
(( my_command 3>&1 1>&2 2>&3 ) | tee error_only.log ) > all.log 2>&1
but replace "error_only.log" with ">(email_command)"
(( my_command 3>&1 1>&2 2>&3 ) | tee >(/bin/mail -s "SUBJECT" "EMAIL") ) > all.log 2>&1
Note: according to tee's docs this will work in bash, but not in /bin/sh. If you're putting this in a cron script (like in /etc/cron.daily/) then you can just but #!/bin/bash at the top. However if you're putting it as a one-liner in a crontab then you may need to wrap it in bash -c ""
If you can do with having stdout/err in separate files, this should do:
($SP_s/StartDailyS1.sh 2>&1 >$LP_s/MirrorLogS1.txt.stdout) | tee $LP_s/MirrorLogS1.txt.stderr
Unless I'm missing something:
command 2>&1 >> file.log | tee -a file.log
2>&1 redirects stderr to stdout
>> redirects regular command stdout to logfile
| tee duplicates stderr (from 2>&1) to logfile and passes it through to stdout be mailed by cron to MAILTO
I tested it with
(echo Hello & echo 1>&2 World) 2>&1 >> x | tee -a x
Which indeed shows World in the console and both texts within x
The ugly thing is the duplicate file name. And the different buffering from stdout/stderr might make text in file.log a bit messy I guess.
A bit tricky if you want stdout and stderr combined in one file, with stderr yet tee'd into its own stream.
This ought to do it (error-checking, clean-up and generalized robustness omitted):
#! /bin/sh
CMD=..../StartDailyS1.sh
LOGFILE=..../MirrorLogS1.txt
FIFO=/tmp/fifo
>$LOGFILE
mkfifo $FIFO 2>/dev/null || :
tee < $FIFO -a $LOGFILE >&2 &
$CMD 2>$FIFO >>$LOGFILE
stderr is sent to a named pipe, picked up by tee(1) where it is appended to the logfile (wherein is also appended your command's stdout) and tee'd back to regular stderr.
My experience (ubuntu) is that 'crontab' only emails 'stderr' (I have the output directed to a log file which is then archived). That is useful, but I wanted a confirmation that the script ran (even when no errors to 'stderr'), and some details about how long it took, which I find is a good way to spot potential trouble.
I found the way I could most easily wrap my head around this problem was to write the script with some duplicate 'echo's in it. The extensive regular 'echo's end up in the log file. For the important non-error bits I want in my crontab 'MAILTO' email, I used an 'echo' that is directed to stderr with '1>&2'.
Thus this:
Frmt_s="+>>%y%m%d %H%M%S($HOSTNAME): " # =Format of timestamp: "<YYMMDD HHMMSS>(<machine name>): "
echo `date "$Frmt_s"`"'$0' started." # '$0' is path & filename
echo `date "$Frmt_s"`"'$0' started." 1>&2 # message to stderr
# REPORT:
echo ""
echo "================================================"
echo "================================================" 1>&2 # message to stderr
TotalMins_i=$(( TotalSecs_i / 60 )) # calculate elapsed mins
RemainderSecs_i=$(( TotalSecs_i-(TotalMins_i*60) ))
Title_s="TOTAL run time"
Summary_s=$Summary_s$'\n'$(printf "%-20s%3s:%02d" "$Title_s" $TotalMins_i $RemainderSecs_i)
echo "$Summary_s"
echo "$Summary_s" 1>&2 # message to stderr
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" 1>&2 # message to stderr
echo ""
echo `date "$Frmt_s"`"TotalSecs_i: $TotalSecs_i"
echo `date "$Frmt_s"`"'$0' concluded." # '$0' is path & filename
echo `date "$Frmt_s"`"'$0' concluded." 1>&2 # message to stderr
Sends me an email containing this (when there are no errors, the lines beginning 'ssh:' and 'rsync:' do not appear):
170408 030001(sb03): '/mnt/data1/LoSR/backup_losr_to_elwd.sh' started.
ssh: connect to host 000.000.000.000 port 0000: Connection timed out
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: unexplained error (code 255) at io.c(226) [Receiver=3.1.0]
ssh: connect to host 000.000.000.000 port 0000: Connection timed out
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: unexplained error (code 255) at io.c(226) [Receiver=3.1.0]
================================================
S6 SUMMARY (mins:secs):
'Templates' 2:07
'Clients' 2:08
'Backups' 0:10
'Homes' 0:02
'NetAppsS6' 10:19
'TemplatesNew' 0:01
'S6Www' 0:02
'Aabak' 4:44
'Aaldf' 0:01
'ateam.ldf' 0:01
'Aa50Ini' 0:02
'Aadmin50Ini' 0:01
'GenerateTemplates' 0:01
'BackupScripts' 0:01
TOTAL run time 19:40
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
170408 031941(sb03): '/mnt/data1/LoSR/backup_losr_to_elwd.sh' concluded.
This doesn't satisfy my initial desire to "send both stdout AND stderr to the logfile" (stderr, and only the 'echo'ed lines with '1>&2' go to the email; stdout goes to the log), but I find this is better than my initially imagined solution, as the email finds me and I don't have to go looking for problems in the log file.
I think the solution would be:
$SP_s/StartDailyS1.sh 2>&1 >> $LP_s/MirrorLogS1.txt | tee -a $LP_s/MirrorLogS1.txt
This will:
append standard output to $LP_s/MirrorLogS1.txt
append standard error to $LP_s/MirrorLogS1.txt
print standard error, so that cron will send a mail in case of error
I assume you are using bash, you redirect stdout and stderr like so
1> LOG_FILE
2> LOG_FILE
to send a mail containing the stderr in the body something like this
2> MESSAGE_FILE
/bin/mail -s "SUBJECT" "EMAIL_ADDRESS" < MESSAGE_FILE
I'm not sure if you can do the above in only one passage as this
/bin/mail -s "SUBJECT" "EMAIL_ADDRESS" <2
You could try writing another cronjob to read the log file and "display" the log (really just let cron email it to you)