I've got the following code, which is supposed to take filenames as arguments, and send them to my email address, which is derived from a username:
#Email Script from linux
#Define username e.g. pp_roman
u="$USER"
#Remove the pp_ and store to variable e.g. roman
u2=${u#"pp_"}
#Define Email portion
em="#workemail.com"
#Combine the username e.g. roman#workemail.com
u3=$u2$em
#Arguments for script
for FILE1 in "$#"
do
filename="-a $FILE1"
done
##This returns the full string with $filename variables for arguments, and email from $u3
mailx $filename -s "Subject" $u3 < /dev/null
However when passing multiple arguments, only the last mentioned filename is sent as an attachment. How do I pass multiple arguments into the $filename variable all appended by "-a"?
Since you are using bash, the right thing to use is an array.
attachments=()
for f in "$#"
do
attachments+=(-a "$f")
done
mailx "${attachments[#]}" -s "Subject" "$u3" < /dev/null
Related
I have a relatively simple BASH script to send mail from my Raspberry Pi. The first argument is the Subject line and the second is a string of data files to be attached.
It is basically working when I specify the message body as a file (line 6). But if I try to create a text sting containing the date as the message body it fails (line7). Here is my script:
#!/bin/bash
#echo $2
# To
TO="me#hotmail.com"
# Message
MESSAGE="output/MessageBody.txt"
MESSAGEx="Midnight `date '+%Y-%m-%d %H:%M:%S %Z'` Pi report"
echo $MESSAGE
echo $MESSAGEx
temp=$(echo $2 | tr ";" "\n")
declare -a attargs
for att in $temp; do
attargs+=( "-A" "$att" )
done
# Sending email using /bin/mail
/usr/bin/mail -s "$1" "$TO" ${attargs[#]} < $MESSAGEx
Here is the output from this command
/usr/pgms/sendtome.sh "test message" "/mnt/usbdrive/output/JSONstart.txt;/mnt/usbdrive/output/Outback_error.log;/mnt/usbdrive/output/OutbackReaderPrint.txt"
when I specify MESSAGEx as the message body:
/mnt/usbdrive/output/MessageBody.txt
Midnight 2019-08-14 07:40:31 MDT Pi report
/usr/pgms/sendtome.sh: line 22: $MESSAGEx: ambiguous redirect
If I use MESSAGE, ie the text file reference, it works.
How can it create a message body text paragraph which contains the date or some other item? Thanks....RDK
There's a number of issues here.
You should generally quote strings. Without quoting, the string after < is split (hence the error message) and the array you took so much care to collect will lose its purpose.
The thing after < needs to be the name of a file. In Bash you can use a here string <<<"$MESSAGEx" but the common and simple portable solution is to echo (or better printf) its value into a pipe.
You should prefer lower case for your private variable names, but this is mainly a stylistic recommendation. (There are reserved variables like PATH and SHELL which you really don't want to clobber; POSIX reserves upper case variable names for system use.)
Here's a refactoring which attempts to address these concerns.
#!/bin/bash
to="me#hotmail.com"
# Message
#msgfile="output/MessageBody.txt"
msgbody="Midnight `date '+%Y-%m-%d %H:%M:%S %Z'` Pi report"
#echo "$msgfile"
#echo "$msgbody"
declare -a attargs
for att in $(echo "$2" | tr ";" "\n"); do
attargs+=( "-A" "$att" )
done
/usr/bin/mail -s "$1" "${attargs[#]}" "$to"<<< "$msgbody"
Perhaps a better design would be to just shift the first argument and then use "$#" as the list of files to attach.
I am working on a bash script and I got a list of IP's that I wanted to add one by one in a CURL command.
For example given list on a file named list.txt
8.8.8.8
10.10.10.10
136.34.24.22
192.168.10.32
I wanted to add each value on curl command
curl -k -u $user:$password "https://logservice/jobs" --data-urlencode 'search=search index=test $ARRAYVALUE | head 1' > output.txt
Where $ARRAYVALUE is the IP address to be used on the command.
I will appreciate any hint.
Thanks
If I understood correctly, you want to:
map each line of a "list.txt" to an item of an array
loop over the newly created array inserting items one by one into your command invocation
Consider this, heavily commented, snippet. Look especially at mapfile and how variable is used in curl invocation, surrounded by double quotes.
#!/bin/bash
# declare a (non-associative) array
# each item is indexed numerically, starting from 0
declare -a ips
#put proper values here
user="userName"
password="password"
# put file into array, one line per array item
mapfile -t ips < list.txt
# counter used to access items with given index in an array
ii=0
# ${#ips[#]} returns array length
# -lt makes "less than" check
# while loops as long as condition is true
while [ ${ii} -lt ${#ips[#]} ] ; do
# ${ips[$ii]} accesses array item with the given (${ii}) index
# be sure to use __double__ quotes around variable, otherwise it will not be expanded (value will not be inserted) but treated as a string
curl -k -u $user:$password "https://logservice/jobs" --data-urlencode "search=search index=test ${ips[$ii]} | head -1" > output.txt
# increase counter to avoid infinite loop
# and access the next item in an array
((ii++))
done
You may read about mapfile in GNU Bash reference: Built-ins.
You may read about creating and accessing arrays in GNU Bash reference: Arrays
Check this great post about quotes in bash.
I hope you found this answer helpful.
I believe you need something like this :
#!/bin/bash
function FN()
{
filename=$1
declare -a IPs_ARRAY
i=0
user=$2
password=$3
while read ip
do
IPs_ARRAY[$i]=$ip
echo ${IPs_ARRAY[$i]}
# Uncomment for your actions ::
#curl -k -u $user:$password "https://logservice/jobs" --data-urlencode 'search=search index=test ${IPs_ARRAY[$i]} | head 1' > output.txt
(( i++ ))
done < $filename
}
#############
### MAIN ###
###########
read -p "Enter username: " username
read -p "Enter password: " password
# Call your function
filename="list.txt"
FN $filename $username $password
I'm trying to combine two lists containing names (if available) and emails with a standard email text in bash (shell)
(I had to delete the irrelevant code as it contains some private info, so some of the code might look unusal.)
The first half of the code checks if there is a name list along with the email list.
The second half combines only the email address and text if no name is available, if the name list is available it also 'tries' to combine the name, email and text.
f1 = email list and f2 = name list.
As you can see in the first half of the code below, $f2 should show the names if the list is available but it does not show anything in the log file.
I been trying to sort this problem out for two days but nothing has worked. When names are available it always outputs as "Hello ..." when it should be "Hello John D..."
#FIRST HALF
if [ "$names" = "no" ]
then
text="Hello..."
elif [ "$names" = "yes" ]
then
text="Hello $f2..."
fi
#SECOND HALF
if [ "$names" = "no" ]
then
for i in $(cat $emaillist); do
echo "$text" >> /root/log
echo "$i" >> /root/log
done
elif [ "$names" = "yes" ]
then
paste $emaillist $namelist | while IFS="$(printf '\t')" read -r f1 f2
do
echo "$text" >> /root/log
echo "$f1" >> /root/log
done
fi
When you run text="Hello $f2", $f2 is looked up at the time of the assignment; an exact string is assigned to text, and only that exact string is used later, on echo "$text".
This is very desirable behavior: If shell variables' values could run arbitrary code, it would be impossible to write shell scripts that handled untrusted data safely... but it does mean that implementing your program requires some changes.
If you want to defer evaluation (looking up the value of $f2 at expansion time rather than assignment), don't use a shell variable at all: Use a function instead.
case $names in
yes) write_greeting() { echo "Hello $name..."; };;
*) write_greeting() { echo "Hello..."; };;
esac
while read -r name <&3 && read -r email <&4; do
write_greeting
echo "$email"
done 3<"$namelist" 4<"$emaillist" >>/root/log
Some enhancements in the code above:
You don't need paste to read from two streams in lockstep; you can simply open them on different file descriptors (above, FDs 3 and 4 are chosen; only 0, 1 and 2 are reserved, so larger numbers could have been selected as well) with a separate read command for each.
Opening your output sink only once for the entire loop (by putting the redirection after the done) is far more efficient than re-opening it every time you want to write a single line.
Expansions, such as "$namelist" and "$emaillist", are always quoted; this makes code more reliable if dealing with filenames with unusual characters (including spaces and glob expressions), or if IFS is at a non-default value.
Having some command-line utility that asks for input several times when you launch it, e.g. here I need to pass username and host to some command.
And I would like to make other command so it will pass these args
user:~$ run_something
username:
host:
How it is possible to pipe these parameters sequentially to command, e.g.:
user:~$ echo "my_user, my_host" | run_something
use xargs
eg.
echo "mydir" | xargs ls
Will list the files in the directory mydir
One way to do this is by using read from standard input:
run_something() {
read -r username host && declare -p username host;
}
then run it as:
echo "my_user my_host" | run_something
to get output:
declare -- username="my_user"
declare -- host="my_host"
Another way is to set variables directly before calling function:
run_something() {
declare -p username host
}
then run it as:
username='my_user' host='my_host' run_something
to get the same output:
declare -x username="my_user"
declare -x host="my_host"
You need to send the newlines that you would otherwise type after each line. run_something is expecting two lines of input:
printf '%s\n' "username" "host" | run_something
I'm fairly new to Linux/CentOS and shell scripting. Let's say I have a file named useraccounts.list. This file contains CSV data in this specific order:
first name, middle initial, last name, username, password
for example:
John,N,Snow,seords,cuai2Ohzdh
What I'm trying to do is traverse through this list creating user accounts from the provided data, and with the password at the end of each line. I would like to store it using the passwd command for each individual user.
Here is what I have so far:
#!/bin/sh
for i in 'more useraccounts.list'
do
echo "$1 $2 $3 $4 $5 $6"
done <file.txt
Can someone please help me out?
Before you get to how you will store the password, you need to sort out how you will read the values from the file correctly. In bash, the primary tool for determining how the shell will break a line into individual tokens is the Internal Field Separator (default: space tab newline). The process is called word-splitting The IFS variable allows you to set the characters that will control word-splitting.
You can use that to your advantage in reading lines such as yours by including a comma in IFS. This will allow you to specify individual variable to read each name, initial, last, user name and password into. A while loop is the normal way to accomplish this -- and it allows you to set the IFS for that block of code without effecting the remainder of the script.
An Example, in your case, would be:
#!/bin/bash
[ -z "$1" ] && { ## validate one argument given on command line
printf "error: insufficient input. usage: %s filename.\n" "${0##*/}"
exit 1
}
[ -r "$1" ] || { ## validate it is a readable filename
printf "error: file not found/readable '%s'.\n" "$1"
exit 1
}
## read each line in file separated by ','
# set Internal Field Separator to break on ',' and '\n'
# protect against lack of '\n' on last line with $pw test
while IFS=$',\n' read -r first mi last uname pw || [ -n "$pw" ]; do
printf "name: %-5s %s. %-6s user: %s pass: %s\n" \
"$first" "$mi" "$last" "$uname" "$pw"
## Create User Accounts/Store Password Here...
done <"$1"
exit 0
Input
$ cat dat/useracct.txt
John,N,Snow,seords,cuai2Ohzdh
Jill,O,Rain,reords,cuai3Ohzdh
Jane,P,Sleet,peords,cuai4Ohzdh
Output
$ bash readuserfile.sh dat/useracct.txt
name: John N. Snow user: seords pass: cuai2Ohzdh
name: Jill O. Rain user: reords pass: cuai3Ohzdh
name: Jane P. Sleet user: peords pass: cuai4Ohzdh
You can then create the user accounts with the desired options and store the password any way you like. Let me know if you have any questions.