Using values of variables in on-the-spot shell commands (using ``) - bash

I have a shell script that for-loops over input to get a number and string. If I want to test the number in the loop, can I cut the looped-over variable to get the number? For example, something like:
for line in input
do
num=`cut -f1 $line`
...
done
If not, how else can I accomplish this?

Instead of:
num=`cut -f1 $line`
You can do:
num=$(echo "$line" | cut -f1)
OR else using awk:
num=$(awk '{print $1}' <<< $line)
OR using pure BASH:
num=${line%% *}
Your command cut -f1 $line will try to cut first column from a file named as $line.

Is this what you want instead ?
while read -r number str
do
echo $number;
echo $str;
done < input

Related

Loop through txt file comma-separated and use as variable

I have txt file separated by comma:
2012,wp_fronins.pdf
2013,test789.pdf
2014,ok09report.pdf
I'm trying to extract from the file each value and pass him to CURL command with a condition before.
For example:
if $value1=2012 do
curl "https://onlinesap.org/reports/$valu1/$value2
Any idea ?
Another way to achieve is to read the file directly and cut the rows to get the elements directly.
while read p; do
value1=`echo $p | cut -d',' -f1`
value2=`echo $p | cut -d',' -f2`
if [ $value1 = "2012" ]; then
curl "https://onlinesap.org/reports/$value1/$value2"
fi
# Add More conditional statements here for other value1
done < filename.txt
Since the name of the pdf file (value2) is unique, you may try something like this:
#!/bin/bash
FILENAME=myFile.txt
cat $FILENAME | awk -F',' '{print $2}' | while read value2; do
value1=`grep -w "$value2" $FILENAME | awk -F',' '{print $1}'` # watch the back-tick
if [ $value1 = "2012" ]; then
curl https://onlinesap.org/reports/$value1/$value2
fi
done
Please notice that the whole file is scanned a second time for each line found.
In other words, its complexity is O(n^2)

Pass multiple arguments from a text file to a bash script

I have a bash script that takes three arguments, and I have a text file with three columns. I'd like the script to take the the text file's first, second, and third columns as the first, second, and third arguments of the bash script.
I'm a beginner with shell scripting, and am running fairly simple programs where efficiency is not very important, so simpler fixes would be appreciated :)
Here's an example of what I'd like to do.
I have a text file called food.txt with the following text:
fruits apples bananas
vegetables kale broccoli
meats pork beef
desserts cake pie
And I have a menu.sh script like this
#!/bin/bash
arg_1=$1
arg_2=$2
arg_3=$3
for arg_1 in $(cat food.txt)
do
echo "${arg_1}:${arg_2},${arg_3}"
done
I would like menu.sh to output this:
fruits:apples,bananas
vegetables:kale,broccoli
meats:pork,beef
desserts:cake,pie
But instead it outputs this:
fruits:,
apples:,
bananas:,
vegetables:,
kale:,
...
Thanks in advance for the help!
The right way with bash's read function:
#!/bin/bash
while read -r arg_1 arg_2 arg_3; do
echo "${arg_1}:${arg_2},${arg_3}"
done < food.txt
https://www.gnu.org/software/bash/manual/bash.html#Bash-Builtins
You met bash word spliting.
Check bash FAQ 1
You need to use
while read -r a1 a2 a3; do
echo "$a1:$a2:$a3"
done < file
cat food.txt | while read line
do
header="$(echo "$line" | cut -f1 -d' ')"
fields="$(echo "$line" | cut -f2,3 -d' ' | tr ' ' ,)"
echo "$header:$fields"
done
If you ever add another field / column you can just change that 2,3 to be 2,3,4 or 2-4. If you ever have a variable number of fields per line, you can change it to 2-.
Another interesting (but not optimal) way to achieve this is using IFS var(Internal Field Separator). It's used within bash to tell how to separate fields, in this case, in a for loop:
#!/bin/bash
file=$(cat food.txt)
IFS_OLD=$IFS
IFS=$'\n'
for line in $file; do
name=$(echo "$line" | cut -d' ' -f1 )
element1=$(echo "$line" | cut -d' ' -f2)
element2=$(echo "$line" | cut -d' ' -f3)
echo "$name:$element1,$element2"
done
IFS=$IFS_OLD
Note that is important to backup and restore original IFS value. As I said, is not optimal, the other answers are better, but this still being another option
Alternatively you could do:
#!/bin/bash
IFS=$'\n'
for arg in $(cat food.txt); do
echo $arg | awk '{ print $1":"$2","$3 }'
done
or:
#!/bin/bash
awk '{ print $1":"$2","$3 }' food.txt

bash assign variable to another after operation

I'm trying to print domain and topLeveldomain variables (example.com)
$line = example.com
domain =$line | cut -d. -f 1
topLeveldomain = $line | cut -d. -f 2
However when I try and echo $domain, it doesn't display desired value
test.sh: line 4: domain: command not found
test.sh: line 5: topLeveldomain: command not found
I suggest:
line="example.com"
domain=$(echo "$line" | cut -d. -f 1)
topLeveldomain=$(echo "$line" | cut -d. -f 2)
The right code for this should be:
line="example.com"
domain=$(echo "$line" | cut -d. -f 1)
topLeveldomain=$(echo "$line" | cut -d. -f 2)
Consider the right syntax of bash:
variable=value
(there are no blanks allowed)
if you want to use the content of the variable you have to add a leading $
e.g.
echo $variable
You don't need external tools for this, just do this in bash
$ string="example.com"
# print everything upto first de-limiter '.'
$ printf "${string%%.*}\n"
example
# print everything after first de-limiter '.'
$ printf "${string#*.}\n"
com
Remove spaces around =:
line=example.com # YES
line = example.com # NO
When you create a variable, do not prepend $ to the variable name:
line=example.com # YES
$line=example.com # NO
When using pipes, you need to pass standard output to the next command. Than means, you usually need to echo variables or cat files:
echo $line | cut -d. -f1 # YES
$line | cut -d. -f1 # NO
Use the $() syntax to get the output of a command into a variable:
new_variable=$(echo $line | cut -d. -f1) # YES
new_variable=echo $line | cut -d. -f1 # NO
I would rather use AWK:
domain="abc.def.hij.example.com"
awk -F. '{printf "TLD:%s\n2:%s\n3:%s\n", $NF, $(NF-1), $(NF-2)}' <<< "$domain"
Output
TLD:com
2:example
3:hij
In the command above, -F option specifies the field separator; NF is a built-in variable that keeps the number of input fields.
Issues with Your Code
The issues with your code are due to invalid syntax.
To set a variable in the shell, use
VARNAME="value"
Putting spaces around the equal sign will cause errors. It is a good
habit to quote content strings when assigning values to variables:
this will reduce the chance that you make errors.
Refer to the Bash Guide for Beginners.
this also works:
line="example.com"
domain=$(echo $line | cut -d. -f1)
toplevel=$(cut -d. -f2 <<<$line)
echo "domain name=" $domain
echo "Top Level=" $toplevel
You need to remove $ from line in the beginning, correct the spaces and echo $line in order to pipe the value to cut . Alternatively feed the cut with $line.

Weird bash results using cut

I am trying to run this command:
./smstocurl SLASH2.911325850268888.911325850268896
smstocurl script:
#SLASH2.911325850268888.911325850268896
model=$(echo \&model=$1 | cut -d'.' -f 1)
echo $model
imea1=$(echo \&simImea1=$1 | cut -d'.' -f 2)
echo $imea1
imea2=$(echo \&simImea2=$1 | cut -d'.' -f 3)
echo $imea2
echo $model$imea1$imea2
Result Received
&model=SLASH2911325850268888911325850268896
Result Expected
&model=SLASH2&simImea1=911325850268888&simImea2=911325850268896
What am I missing here ?
You are cutting based on the dot .. In the first case your desired string contains the first string, the one containing &model, so then it is printed.
However, in the other cases you get the 2nd and 3rd blocks (-f2, -f3), so that the imea text gets cutted off.
Instead, I would use something like this:
while IFS="." read -r model imea1 imea2
do
printf "&model=%s&simImea1=%s&simImea2=%s\n" $model $imea1 $imea2
done <<< "$1"
Note the usage of printf and variables to have more control about what we are writing. Using a lot of escapes like in your echos can be risky.
Test
while IFS="." read -r model imea1 imea2; do printf "&model=%s&simImea1=%s&simImea2=%s\n" $model $imea1 $imea2
done <<< "SLASH2.911325850268888.911325850268896"
Returns:
&model=SLASH2&simImea1=911325850268888&simImea2=911325850268896
Alternatively, this sed makes it:
sed -r 's/^([^.]*)\.([^.]*)\.([^.]*)$/\&model=\1\&simImea1=\2\&simImea2=\3/' <<< "$1"
by catching each block of words separated by dots and printing back.
You can also use this way
Run:
./program SLASH2.911325850268888.911325850268896
Script:
#!/bin/bash
String=`echo $1 | sed "s/\./\&simImea1=/"`
String=`echo $String | sed "s/\./\&simImea2=/"`
echo "&model=$String
Output:
&model=SLASH2&simImea1=911325850268888&simImea2=911325850268896
awk way
awk -F. '{print "&model="$1"&simImea1="$2"&simImea2="$3}' <<< "SLASH2.911325850268888.911325850268896"
or
awk -F. '$0="&model="$1"&simImea1="$2"&simImea2="$3' <<< "SLASH2.911325850268888.911325850268896"
output
&model=SLASH2&simImea1=911325850268888&simImea2=911325850268896

get a part of string just like accessing an array?

In shell,
s="abc\tdef\tghi" # 3 words separated by \t
What if I want to get the second word which is def?
PS
I know cut can do the job, but any way else just like variable substitution?
How about cut ?
[cnicutar#ariel ~]$ echo -e $s | cut -f2
def
Or maybe awk:
echo -e $s | awk '{print $2}'
Maybe you're looking for this.
s="abc\tdef\tghi"
s=${s#*\t}
s=${s%\\t*}
echo $s

Resources