How to label std output in a bash script [duplicate] - bash

This question already has an answer here:
List content of one or more files with a header showing the file name
(1 answer)
Closed 7 years ago.
I have a basic script that looks like this:
#!/bin/bash
### run these 2 commands and dump the contents to a file
run command 1 > /dir/file1
run command 2 > /dir/file2
##### run this command, echo the output and email me if $var is above a certain # and send the contents of the dump files in the email
var=$(netstat -an | wc -l)
echo $var
if [ "$var" -ge 700 ]; then
cat /dir/file1 /dir/file2 | mailx -s "System X has over $var connections. " email recipients
fi
What I need to do is label the contents to distinguish between the 2 files so when I get the email I know what output came from command 1 and what came from command 2. What is the best way to do this?

You can abuse head:
$ cat file1
Contents of file 1
$ cat file2
Contents of file 2
$ head -n -0 file*
==> file1 <==
Contents of file 1
==> file2 <==
Contents of file 2

Try:
{ echo "From command 1:"; cat /dir/file1; echo "From command 2:"; cat /dir/file2; } | mailx -s "System X has over $var connections." email recipients

Related

bash echo is doing commands and not adding what I need [duplicate]

This question already has an answer here:
echo bash code to .sh file to execute mixed up the variables
(1 answer)
Closed 3 months ago.
I need to create a new file name test.sh.
I need to write those lines with echo or somethings else but cant open new file manually and write it.
That is, I have to write down some things in the bash such a way that the following file is created:
TEST_VALUE=$1
if [[cat data | grep $TEST_VALUE]]; then
exit 1
fi
exit 0
But, when I do that by echo the result is:
TEST_VALUE=
if [[]]; then
exit 1
fi
exit 0
I need the file as I write it with $1 and not the argument and with the grep.
I tried to grep each row but it is doing the command and not copied it as I want.
How do I do it?
Thank You
Ignoring that the content you're trying to copy is buggy, the best way to do this is with a quoted heredoc:
cat >file <<'EOF'
TEST_VALUE=$1
if [[cat data | grep $TEST_VALUE]]; then
exit 1
fi
exit 0
EOF
But that content is buggy! A better version would look like:
cat >file <<'EOF'
#!/bin/sh
test_value=$1
! grep -q -e "$test_value" <data
EOF
echo 'TEST_VALUE=$1
if [[cat data | grep $TEST_VALUE]]; then
exit 1
fi
exit 0' > test.sh
You need to use quotes, in any other case your command will be executed.

Testing whether stdin is a file vs. a pipe vs. a tty

I know for bash and zsh, one can use e.g. [ -t 1 ] to determine if STDIN is an interactive tty session.
However, there doesn't seem to be a way to test whether stdin is being redirected from a file, versus being piped in from a command:
foo < ./file
bar | foo
Is there any way to detect the difference between these two? Separately, is there any way to get the path of the file being redirected from (outside of /proc/self, which is unavailable on macOS)?
You can check if /dev/stdin is a regular file or a pipe:
$ cat tmp.sh
#!/bin/bash
if [ -f /dev/stdin ]; then
echo "file"
elif [ -p /dev/stdin ]; then
echo "pipe"
fi
$ bash tmp.sh < foo.txt
file
$ echo foo | bash tmp.sh
pipe
This relies on /dev/stdin being in your file system, though.
You can also use the stat command, which will return information about standard input given no file name argument. As you mentioned you are using macOS, you can use the %HT format:
$ stat -f %HT
Character Device
$ stat -f %HT < foo.txt
Regular File
$ echo foo | stat -f %HT
Fifo File

shell script - run list of commands

for i in `cat foo.txt`
do
$i
done
And I have a input file "foo.txt", with list of commands.
ls -ltr | tail
ps -ef | tail
mysql -e STATUS | grep "^Uptime"
when I run the shell script, it executes, but splits the commands in each line at spaces i.e for first line it executes only "ls", then "-ltr" for which I get command not found error.
How can I run each list as one command?
why am I doing this?
I execute lot of arbitrary shell commands including DB commands. I need to have a error handling as I execute each command(each line from foo.txt), I can't think of what can go wrong, so the idea is put all commands in order and call them in loop and check for error (#?) at each line and stop on error.
Why not just do this?
set -e
. ./foo.txt
set -e causes the shell script to abort if a command exits with a non-zero exit code, and . ./foo.txt executes commands from foo.txt in the current shell.
but I guess I can't send notification (email).
Sure you can. Just run the script in a subshell, and then respond to the result code:
#!/bin/sh
(
set -e
. ./foo.txt
)
if [ "$?" -ne 0 ]; then
echo "The world is on fire!" | mail -s 'Doom is upon us' you#youremail.com
fi
Code mentioned.
for i in `cat foo.txt`
do
$i
done
Please use https://www.shellcheck.net/
This will result _
$ shellcheck myscript
Line 1:
for i in `cat foo.txt`
^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang.
^-- SC2013: To read lines rather than words, pipe/redirect to a 'while read' loop.
^-- SC2006: Use $(...) notation instead of legacy backticked `...`.
Did you mean: (apply this, apply all SC2006)
for i in $(cat foo.txt)
$
Will try while loop, and for test purpose content of foo.txt mentioned below
cat foo.txt
ls -l /tmp/test
ABC
pwd
while read -r line; do $line; if [ "$?" -ne 0 ]; then echo "Send email Notification stating $line Command reported error "; fi; done < foo.txt
total 0
-rw-r--r--. 1 root root 0 Dec 24 11:41 test.txt
bash: ABC: command not found...
Send email Notification stating ABC Command reported error
/tmp
In case error reported you can break the loop.
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_05.html
while read -r line; do $line; if [ "$?" -ne 0 ]; then echo "Send email Notification stating $line Command reported error "; break; fi; done < foo.txt
total 0
-rw-r--r--. 1 root root 0 Dec 24 11:41 test.txt
bash: ABC: command not found...
Send email Notification stating ABC Command reported error
while read -r line; do eval $line; if [ "$?" -ne 0 ]; then echo "Send email Notification stating $line Command reported error "; break; fi; done < foo.txt
total 0
-rw-r--r--. 1 root root 0 Dec 24 11:41 test.txt
bash: ABC: command not found...
Send email Notification stating ABC Command reported error

How do I use the file redirected to the standard input of a bash script?

How do I use the file redirected to the standard input of a bash script?
$ script.sh < file_to_use_in_script
what do I have to put in my script, so that I can write the filename in a variable from this input, without knowing the pathname beforehand.
FILENAME=$file_to_use_in_script
You can use the filename /dev/stdin. Make sure to only read it once.
$ cat myscript
#!/bin/bash
file=${1:-/dev/stdin}
echo "Reading from $file"
nl "$file"
$ cat myfile
hello world
$ ./myscript myfile
Reading from myfile
1 hello world
$ ./myscript < myfile
Reading from /dev/stdin
1 hello world
$ echo "something" | ./myscript
Reading from /dev/stdin
1 something
i would suggest to pass the filename as a an argument, you obviously know it anyway
echo "name $1"
while read line
do
echo $line
done
and then:
./test.sh foo/bar.txt < foo/bar.txt
gives
name foo/bar.txt
1
2
3
if foo/bar.txt contains
1
2
3

Shell scripting, store command output in variable and preserve formatting [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Capturing multiple line output to a bash variable
I have what is probably a basic scripting question, but I haven't been able to find an answer anywhere that I've looked.
I have an awk script that processes a file, and spits out a list of disabled systems. When I call it manually from the command line, I get formatted output back:
$awk -f awkscript datafile
The following notifications are currently disabled:
host: bar
host: foo
I am writing a wrapper script to call from my crontab, which will run the awk script, determine if there is any output, and email me if there is. It looks like (simplified):
BODY=`awk -f awkscript datafile`
if [ -n "$BODY" ]
then
echo $BODY | mailx -s "Disabled notifications" me#mail.address
else
echo "Nothing is disabled"
fi
When run this way, and confirmed by adding an echo $BODY into the script, the output is stripped of the formatting (newlines are mainly what I'm concerned with), so I get output that looks like:
The following notitifications are currently disabled: host: bar host: foo
I'm trying to figure out how to preserve the formatting that is present if I run the command manually.
Things I've tried so far:
echo -e `cat datafile | awkscript` > /tmp/tmpfile
echo -e /tmp/tmpfile
I tried this because on my system (Solaris 5.10), using echo without the -e ignores standard escape sequences like \n . Didn't work. I checked the tmpfile, and it doesn't have any formatting in it, so the problem is happening when storing the output, not when printing it out.
BODY="$(awk -f awkscript datafile)"
echo -e "$BODY"
I tried this because everything I could find, including some other questions here on stackoverflow said that the problem was that the shell would replace whitespace codes with spaces if it wasn't quoted. Didn't work.
I've tried using printf instead of echo, using $(command) instead of `command`, and using a tempfile instead of a variable to store the output, but nothing seems to retain the formatting.
What am I missing, or is there another way to do this which avoids this problem all together?
BODY=`awk -f awkscript datafile`
if [ -n "$BODY" ]
then echo "$BODY" | mailx -s "Disabled notifications" me#mail.address
else echo "Nothing is disabled"
fi
Note the double quotes in the echo.
You can simplify this version, too:
echo -e `cat datafile | awkscript` > /tmp/tmpfile
echo -e /tmp/tmpfile
to just:
tmpfile=/tmp/tmpfile.$$
awkscript > $tmpfile
if [ -s $tmpfile ]
then mailx -s "Disabled notifications" me#mail.address < $tmpfile
else echo "Nothing is disabled"
fi
Backquotes are useful (but better written as $(cmd args)) but do not have to be used everywhere.
Using quotes should work, and does for me:
$ cat test.sh
#!/bin/bash -e
BODY=`cat test.in`
if [ -n "$BODY" ]; then
echo "$BODY" | mailx -s "test" username
else
echo "Nothing"
fi
$ cat test.in
The following notifications are currently disabled:
host: bar
host: foo
$ ./test.sh
$
And this sends me a properly formatted email.

Resources