Basic bash script arguments - arrays

I'm new to bash script and wanted to know how to do the following. Basically I have the following line
./application [channels] -> [message]
where I would like to store these into 2 bash variables with the '->' as the delimiter.
ie:
$channels = channels
$message = message
Furthermore, there can be multiple channels separated by a , and/or a space. So the full line would be something like this:
./application Channel1, Channel2,Channel3 -> This is a message.
Now in bash, I would like the channels stored in a channels array separated by , (and get rid of the space if its there) and message stored in a message variable. The only thing I can put together at the moment is this:
#!/bin/bash
for i
do
echo $i
done
can someone help me out?

Since you're likely to have spaces in your message, keep your arguments in quotes, and you keep it simple:
for CHANNEL in $(echo $1 | tr " ," "\n")
do
echo Channel: ${CHANNEL}
done
echo Message: ${2}
Example:
anew#buddha:~/dev/so$ bash example.sh "channel1 channel2 channel3" "this is the message"
Channel: channel1
Channel: channel2
Channel: channel3
Message: this is the message

Related

Why does "echo $array" print all members of the array in this specific case instead of only the first member like in any other case?

I have encountered a very curious problem, while trying to learn bash.
Usually trying to print an echo by simply parsing the variable name like this only outputs the first member Hello.
#!/bin/bash
declare -a test
test[0]="Hello"
test[1]="World"
echo $test # Only prints "Hello"
BUT, for some reason this piece of code prints out ALL members of the given array.
#!/bin/bash
declare -a files
counter=0
for file in "./*"
do
files[$counter]=$file
let $((counter++))
done
echo $files # prints "./file1 ./file2 ./file3" and so on
And I can't seem to wrap my head around it on why it outputs the whole array instead of only the first member. I think it has something to do with my usage of the foreach-loop, but I was unable to find any concrete answer. It's driving me crazy!
Please send help!
When you quoted the pattern, you only created a single entry in your array:
$ declare -p files
declare -a files=([0]="./*")
If you had quoted the parameter expansion, you would see
$ echo "$files"
./*
Without the quotes, the expansion is subject to pathname generation, so echo receives multiple arguments, each of which is printed.
To build the array you expected, drop the quotes around the pattern. The results of pathname generation are not subject to further word-splitting (or recursive pathname generation), so no quotes would be needed.
for file in ./*
do
...
done

How can I split bash CLI arguments into two separate arrays for later usage?

New to StackOverflow and new to bash scripting. I have a shell script that is attempting to do the following:
cd into a directory on a remote machine. Assume I have already established a successful SSH connection.
Save the email addresses from the command line input (these could range from 1 to X number of email addresses entered) into an array called 'emails'
Save the brand IDs (integers) from the command line input (these could range from 1 to X number of brand IDs entered) into an array called 'brands'
Use nested for loops to iterate over the 'emails' and 'brands' arrays and add each email address to each brand via add.py
I am running into trouble splitting up and saving data into each array, because I do not know where the command line indices of the emails will stop, and where the indices of the brands will begin. Is there any way I can accomplish this?
command line input I expect to look as follows:
me#some-remote-machine:~$ bash script.sh person1#gmail.com person2#gmail.com person3#gmail.com ... personX#gmail.com brand1 brand2 brand3 ... brandX
The contents of script.sh look like this:
#!/bin/bash
cd some/directory
emails= ???
brands= ???
for i in $emails
do
for a in $brands
do
python test.py add --email=$i --brand_id=$a --grant=manage
done
done
Thank you in advance, and please let me know if I can clarify or provide more information.
Use a sentinel argument that cannot possibly be a valid e-mail address. For example:
$ bash script.sh person1#gmail.com person2#gmail.com '***' brand1 brand2 brand3
Then in a loop, you can read arguments until you reach the non-email; everything after that is a brand.
#!/bin/bash
cd some/directory
while [[ $1 != '***' ]]; do
emails+=("$1")
shift
done
shift # Ignore the sentinal
brands=( "$#" ) # What's left
for i in "${emails[#]}"
do
for a in "${brands[#]}"
do
python test.py add --email="$i" --brand_id="$a" --grant=manage
done
done
If you can't modify the arguments that will be passed to script.sh, then perhaps you can distinguish between an address and a brand by the presence or absence of a #:
while [[ $1 = *#* ]]; do
emails+=("$1")
shift
done
brands=("$#")
I'm assuming that the number of addresses and brands are independent. Otherwise, you can simply look at the total number of arguments $#. Say there are N of each. Then
emails=( "${#:1:$#/2}" ) # First half
brands=( "${#:$#/2+1}" ) # Second half

Splitting string separated by comma into array values in shell script?

My data set(data.txt) looks like this [imageID,sessionID,height1,height2,x,y,crop]:
1,0c66824bfbba50ee715658c4e1aeacf6fda7e7ff,1296,4234,194,1536,0
2,0c66824bfbba50ee715658c4e1aeacf6fda7e7ff,1296,4234,194,1536,0
3,0c66824bfbba50ee715658c4e1aeacf6fda7e7ff,1296,4234,194,1536,0
4,0c66824bfbba50ee715658c4e1aeacf6fda7e7ff,1296,4234,194,1536,950
These are a set of values which I wish to use. I'm new to shell script :) I read the file line by line like this ,
cat $FILENAME | while read LINE
do
string=($LINE)
# PROCESSING THE STRING
done
Now, in the code above, after getting the string, I wish to do the following :
1. Split the string into comma separated values.
2. Store these variables into arrays like imageID[],sessionID[].
I need to access these values for doing image processing using imagemagick.
However, I'm not able to perform the above steps correctly
set -A doesn't work for me (probably due to older BASH on OSX)
Posting an alternate solution using read -a in case someone needs it:
# init all your individual arrays here
imageId=(); sessionId=();
while IFS=, read -ra arr; do
imageId+=(${arr[0]})
sessionId+=(${arr[1]})
done < input.csv
# Print your arrays
echo "${imageId[#]}"
echo "${sessionId[#]}"
oIFS="$IFS"; IFS=','
set -A str $string
IFS="$oIFS"
echo "${str[0]}";
echo "${str[1]}";
echo "${str[2]}";
you can split and store like this
have a look here for more on Unix arrays.

Handle ctrl +d programmatically?

I am trying to execute the following perl script.
##some code
$command = "nail -s this is a test $email";
system($command);
##some code
when I run this script, it hangs until I press CtrlD. after pressing CtrlD I get the desired result. My question is how can I hardcode CtrlD in my script?
I suppose you call mailx. nail ist most likely an alias. It expects input from STDIN, which is ended with CtrlD. You could workaround like this to send an empty mail:
$command = 'echo "" | nail -s SUBJECT ' . $email;
The mail program expects an . on a line alone to show it is the end of the message
Just make sure your $email contains a \n. and it should no longer hang.
The usual solution is to redirect it to read from /dev/null
Try to use this :
mail -s "Hello Test" -a Attachment email-address </dev/null
or, if you have any email body
mail -s "Hello Test" -a Attachment email-address <emailbodyfile.txt

How to send array values by mail

in linux scripting,
is there a way to use mail function to one time send array values??
function my_mail_function(){
# send array values
mail array_values_here "mymail#domain.tld" ;
}
Thank you
You can step through an array with just a little bit of bash code.
#!/bin/bash
# Here's a simple array...
a=(one two three)
# The brackets encapsulate multiple commands to feed to the stdin of sendmail
(
echo "To: Mister Target <target#example.com>"
echo "From: Julio Fong <jf#example.net>"
echo "Subject: Important message!"
echo ""
count=1
for item in ${a[#]}; do
printf "Value %d is %s\n" "$count" "$item"
((count++))
done
echo ""
) | /usr/sbin/sendmail -oi -fjf#example.net target#example.com
Note that it'll be safer to use sendmail directly rather than relying on the availability and configuration of a mail or Mail command. Your sendmail binary may not be in the same place as mine; if /usr/sbin/ doesn't work for you, check /usr/libexec/. It'll depend on the distribution of Linux you're running.
The proper way to use mail is:
mail -s "subject here" recipient1 recipient2 ...
the command reads the email body from stdin so you can format it how you like and read it in from a pipe or a here-doc or a file or ...
function my_mail_function(){
printf "%s\n" "${array_var[#]}" | mail -s "array values" mymail#domain.tld
}

Resources