How to print counter and comma separated field in bash? - bash

I have limited experience with Shell scripting. I was trying to print the comma-separated field with their index number.
I found a similar question here Variables in bash seq replacement ({1..10}) .
IN="abc,def,123"
for i in $(echo "$IN" | tr "," "\n")
do
echo $i
done
How can we also print the counter number?
My attempt:
count=1
for i in $(echo "$IN" | tr "," "\n")
do
echo $count $i
count+=1
done
But this does not work.

You need to use the let command to perform arithmetic:
let count+=1
or an arithmetic expression:
((count+=1))
BTW, there's no need to use tr to split the input on commas, you can set IFS.
saveIFS=$IFS
IFS=,
for i in $IN
do
echo $i
done
IFS=$saveIFS

Related

program that prints input in bash

I am new to bash and I struggling with a program. I want to write a program that first asks for user input and afterwards prints the words with an \n(blank line) between them. The last echo contains the amount of characters that is written. Also the output can only contain the words and no digits. E.g:
Input: hallo1 user2 Pete4
Ouput: hallo
user
Pete
13 Characters
This is my code for the time beeing.
echo Typ one or multiple words:
read varname
arr=( "${arr[#]}" "$varname" )
for i in "${arr[#]}"; do
echo "$i"
done
echo ${arr[#]}
# printf '%s\n' "${arr[#]}"
Try this. Works for me. I added in the for the sentence to remove the digits.
And after the for, I first remove the spaces between the names and then I count the total of characters using the # in ${#aux}. I added the parameter -n in the first echo too, just to break the line with the second one.
echo Type one or multiple words:
read varname
arr=( "${arr[#]}" "$varname" )
for i in "${arr[#]//[[:digit:]]/}"; do
echo -n "$i"
done
aux=$(echo "${i}" | sed "s/ //g")
echo " " ${#aux} " Characters"
An approach in plain bash without using an array:
#!/bin/bash
echo 'Type one or multiple words on a line:'
read -r
words_without_digits=${REPLY//[0-9]}
line_without_blanks=${words_without_digits//[[:blank:]]}
printf '%s\n' $words_without_digits
echo "${#line_without_blanks} Characters"
echo Type one or multiple words:
read varname
arr=( "${arr[#]}" "$varname" )
for i in "${arr[#]//[[:digit:]]/}"; do
printf '%s\n' $i
done
aux=$(echo "${i}" | sed "s/ //g")
echo ${#aux} " Characters"

Split a string in bash based on delimiter

I have a file log_file which has contents such as
CCO O-MR1 Sync:No:3:No:346:Yes
CCO P Sync:No:1:No:106:Yes
CCO P Checkout:Yes:1:No:10:No
CCO O-MR1 Checkout(2.2):Yes:1:No:10:No
I am trying to obtain the 4 fields based on ":" delimiter
The script that I have is
#!/bin/bash
log_file=$1
for i in `cat $log_file` ; do
echo $i
field_a=`echo $i | awk -F '[:]' '{print $1}'`
echo $field_a
field_b=`echo $i | awk -F '[:]' '{print $2}'`
echo $lfield_b
...
done
but the value that this code gives for field_a is wrong, it splits the line based on " " delimiter.
echo $i also prints wrong value.
What else can I use to correct this?
This is covered in detail in BashFAQ #1. To summarize, use a while read loop with IFS set to contain (only) the characters that should be used to split fields.
while IFS=: read -r field_a field_b other_fields; do
echo "field_a is $field_a"
echo "field_b is $field_b"
echo "Remaining fields are $other_fields"
done <"$log_file"

Loop through a comma-separated shell variable

Suppose I have a Unix shell variable as below
variable=abc,def,ghij
I want to extract all the values (abc, def and ghij) using a for loop and pass each value into a procedure.
The script should allow extracting arbitrary number of comma-separated values from $variable.
Not messing with IFS
Not calling external command
variable=abc,def,ghij
for i in ${variable//,/ }
do
# call your procedure/other scripts here below
echo "$i"
done
Using bash string manipulation http://www.tldp.org/LDP/abs/html/string-manipulation.html
You can use the following script to dynamically traverse through your variable, no matter how many fields it has as long as it is only comma separated.
variable=abc,def,ghij
for i in $(echo $variable | sed "s/,/ /g")
do
# call your procedure/other scripts here below
echo "$i"
done
Instead of the echo "$i" call above, between the do and done inside the for loop, you can invoke your procedure proc "$i".
Update: The above snippet works if the value of variable does not contain spaces. If you have such a requirement, please use one of the solutions that can change IFS and then parse your variable.
If you set a different field separator, you can directly use a for loop:
IFS=","
for v in $variable
do
# things with "$v" ...
done
You can also store the values in an array and then loop through it as indicated in How do I split a string on a delimiter in Bash?:
IFS=, read -ra values <<< "$variable"
for v in "${values[#]}"
do
# things with "$v"
done
Test
$ variable="abc,def,ghij"
$ IFS=","
$ for v in $variable
> do
> echo "var is $v"
> done
var is abc
var is def
var is ghij
You can find a broader approach in this solution to How to iterate through a comma-separated list and execute a command for each entry.
Examples on the second approach:
$ IFS=, read -ra vals <<< "abc,def,ghij"
$ printf "%s\n" "${vals[#]}"
abc
def
ghij
$ for v in "${vals[#]}"; do echo "$v --"; done
abc --
def --
ghij --
I think syntactically this is cleaner and also passes shell-check linting
variable=abc,def,ghij
for i in ${variable//,/ }
do
# call your procedure/other scripts here below
echo "$i"
done
#/bin/bash
TESTSTR="abc,def,ghij"
for i in $(echo $TESTSTR | tr ',' '\n')
do
echo $i
done
I prefer to use tr instead of sed, becouse sed have problems with special chars like \r \n in some cases.
other solution is to set IFS to certain separator
Another solution not using IFS and still preserving the spaces:
$ var="a bc,def,ghij"
$ while read line; do echo line="$line"; done < <(echo "$var" | tr ',' '\n')
line=a bc
line=def
line=ghij
Here is an alternative tr based solution that doesn't use echo, expressed as a one-liner.
for v in $(tr ',' '\n' <<< "$var") ; do something_with "$v" ; done
It feels tidier without echo but that is just my personal preference.
The following solution:
doesn't need to mess with IFS
doesn't need helper variables (like i in a for-loop)
should be easily extensible to work for multiple separators (with a bracket expression like [:,] in the patterns)
really splits only on the specified separator(s) and not - like some other solutions presented here on e.g. spaces too.
is POSIX compatible
doesn't suffer from any subtle issues that might arise when bash’s nocasematch is on and a separator that has lower/upper case versions is used in a match like with ${parameter/pattern/string} or case
beware that:
it does however work on the variable itself and pop each element from it - if that is not desired, a helper variable is needed
it assumes var to be set and would fail if it's not and set -u is in effect
while true; do
x="${var%%,*}"
echo $x
#x is not really needed here, one can of course directly use "${var%%:*}"
if [ -z "${var##*,*}" ] && [ -n "${var}" ]; then
var="${var#*,}"
else
break
fi
done
Beware that separators that would be special characters in patterns (e.g. a literal *) would need to be quoted accordingly.
Here's my pure bash solution that doesn't change IFS, and can take in a custom regex delimiter.
loop_custom_delimited() {
local list=$1
local delimiter=$2
local item
if [[ $delimiter != ' ' ]]; then
list=$(echo $list | sed 's/ /'`echo -e "\010"`'/g' | sed -E "s/$delimiter/ /g")
fi
for item in $list; do
item=$(echo $item | sed 's/'`echo -e "\010"`'/ /g')
echo "$item"
done
}
Try this one.
#/bin/bash
testpid="abc,def,ghij"
count=`echo $testpid | grep -o ',' | wc -l` # this is not a good way
count=`expr $count + 1`
while [ $count -gt 0 ] ; do
echo $testpid | cut -d ',' -f $i
count=`expr $count - 1 `
done

Bash: split by comma with special characters

I have a list that is comma delimited like so...
00:00:00:00:00:00,Bob's Laptop,11111111111111111
00:00:00:00:00:00,Mom & Dad's Computer,22222222222222222
00:00:00:00:00:00,Kitchen,33333333333333333
I'm trying to loop over these lines and populate variables with the 3 columns in each row. My script works when the data has no spaces, ampersands, or apostrophes. When it does have those then it doesn't work right. Here is my script:
for line in $(cat list)
do
arr=(`echo $line | tr "," "\n"`)
echo "Field1: ${arr[0]}"
echo "Field2: ${arr[1]}"
echo "Field3: ${arr[2]}"
done
If one of you bash gurus can point out how I can get this script to work with my list I would greatly appreciate it!
EV
while IFS=, read field1 field2 field3
do
echo $field1
echo $field2
echo $field3
done < list
Can you use awk?
awk -F',' '{print "Field1: " $1 "\nField2: " $2 "\nField3: " $3}'
Do not read lines with a for loop. Use read instead
while IFS=, read -r -a line;
do
printf "%s\n" "${line[0]}" "${line[1]}" "${line[2]}";
done < list
Or, using array slicing
while IFS=, read -r -a line;
do
printf "%s\n" "${line[#]:0:3}";
done < list

Combine path + list of files to list of absolute files

If I have this data in my shell script:
DIR=/opt/app/classes
JARS=a.jar:b.jar:c.jar
How can I combine this to the string
/opt/app/classes/a.jar:/opt/app/classes/b.jar:/opt/app/classes/c.jar
in Shell/Bash scripting?
Here's a very short one:
$ echo "$DIR/${JARS//:/:$DIR/}"
/opt/app/classes/a.jar:/opt/app/classes/b.jar:/opt/app/classes/c.jar
If you don't mind an extra semicolon at the end:
[~]> for a in `echo $JARS | tr ":" "\n"`;do echo -n $DIR/$a:;done&&echo
/opt/app/classes/a.jar:/opt/app/classes/b.jar:/opt/app/classes/c.jar:
Use translate and iterate through the results. Then trim the result ':' character at the beginning of the string.
#! /bin/bash
DIR=/opt/app/classes
JARS=a.jar:b.jar:c.jar
for i in $(echo $JARS | tr ":" "\n")
do
result=$result:$DIR/$i
done
echo ${result#:} // Remove the starting :
Result:
/opt/app/classes/a.jar:/opt/app/classes/b.jar:/opt/app/classes/c.jar
Pure Optimized Bash 1-liner
IFS=:; set -- $JARS; for jar; do path+=$DIR/${jar}:; done; echo "$path"
Output
/opt/app/classes/a.jar:/opt/app/classes/b.jar:/opt/app/classes/c.jar:
Pure Bash, no external utilities:
saveIFS=$IFS
IFS=:
jararr=($JARS)
echo "${jararr[*]/#/$DIR/}"
IFS=saveIFS
Original Answer (before question was revised):
IFS=: read -ra jararr <<<"$JARS"
newarr=(${jararr[#]/#/$DIR/})
echo "${newarr[0]}:${newarr[1]}"

Resources