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"
Related
I want to input multiple strings.
For example:
abc
xyz
pqr
and I want output like this (including quotes) in a file:
"abc","xyz","pqr"
I tried the following code, but it doesn't give the expected output.
NextEmail=","
until [ "a$NextEmail" = "a" ];do
echo "Enter next E-mail: "
read NextEmail
Emails="\"$Emails\",\"$NextEmail\""
done
echo -e $Emails
This seems to work:
#!/bin/bash
# via https://stackoverflow.com/questions/1527049/join-elements-of-an-array
function join_by { local IFS="$1"; shift; echo "$*"; }
emails=()
while read line
do
if [[ -z $line ]]; then break; fi
emails+=("$line")
done
join_by ',' "${emails[#]}"
$ bash vvuv.sh
my-email
another-email
third-email
my-email,another-email,third-email
$
With sed and paste:
sed 's/.*/"&"/' infile | paste -sd,
The sed command puts "" around each line; paste does serial pasting (-s) and uses , as the delimiter (-d,).
If input is from standard input (and not a file), you can just remove the input filename (infile) from the command; to store in a file, add a redirection at the end (> outfile).
If you can withstand a trailing comma, then printf can convert an array, with no loop required...
$ readarray -t a < <(printf 'abc\nxyx\npqr\n' )
$ declare -p a
declare -a a=([0]="abc" [1]="xyx" [2]="pqr")
$ printf '"%s",' "${a[#]}"; echo
"abc","xyx","pqr",
(To be fair, there's a loop running inside bash, to step through the array, but it's written in C, not bash. :) )
If you wanted, you could replace the final line with:
$ printf -v s '"%s",' "${a[#]}"
$ s="${s%,}"
$ echo "$s"
"abc","xyx","pqr"
This uses printf -v to store the imploded text into a variable, $s, which you can then strip the trailing comma off using Parameter Expansion.
I have the following code:
for x in "${array[#]}"
do
echo "$x"
done
The results are something like this (I sort these later in some cases):
1
2
3
4
5
Is there a way to print it as 1 2 3 4 5 instead? Without adding a newline every time?
Yes. Use the -n option:
echo -n "$x"
From help echo:
-n do not append a newline
This would strips off the last newline too, so if you want you can add a final newline after the loop:
for ...; do ...; done; echo
Note:
This is not portable among various implementations of echo builtin/external executable. The portable way would be to use printf instead:
printf '%s' "$x"
printf '%s\n' "${array[#]}" | sort | tr '\n' ' '
printf '%s\n' -- more robust than echo and you want the newlines here for sort's sake
"${array[#]}" -- quotes unnecessary for your particular array, but good practice as you don't generally want word-spliting and glob expansions there
You don't need a for loop to sort numbers from an array.
Use process substitution like this:
sort <(printf "%s\n" "${array[#]}")
To remove new lines, use:
sort <(printf "%s\n" "${array[#]}") | tr '\n' ' '
You can also do it this way:
array=(1 2 3 4 5)
echo "${array[#]}"
If, for whatever reason, -n doesn't fix this for you, you can also add \c to the end of the thing to be echo'd:
echo "$x\c"
I have the following at the moment:
for file in *
do
list="$list""$file "`cat $file | wc -l | sort -k1`$'\n'
done
echo "$list"
This is printing:
fileA 10
fileB 20
fileC 30
I would then like to cycle through $list and cut column 2 and perform calculations.
When I do:
for line in "$list"
do
noOfLinesInFile=`echo "$line" | cut -d\ -f2`
echo "$noOfLinesInFile"
done
It prints:
10
20
30
BUT, the for loop is only being entered once. In this example, it should be entering the loop 3 times.
Can someone please tell me what I should do here to achieve this?
If you quote the variable
for line in "$list"
there is only one word, so the loop is executed just once.
Without quotes, $line would be populated with any word found in the $list, which is not what you want, either, as it would process the values one by one, not lines.
You can set the $IFS variable to newline to split $list on newlines:
IFS=$'\n'
for line in $list ; do
...
done
Don't forget to reset IFS to the original value - either put the whole part into a subshell (if no variables should survive the loop)
(
IFS=$'\n'
for ...
)
or backup the value:
IFS_=$IFS
IFS=$'\n'
for ...
IFS=$IFS_
...
done
This is because list in shell are just defined using space as a separator.
# list="a b c"
# for i in $list; do echo $i; done
a
b
c
# for i in "$list"; do echo $i; done
a b c
in your first loop, you actually are not building a list in shell sens.
You should setting other than default separators either for the loop, in the append, or in the cut...
Use arrays instead:
#!/bin/bash
files=()
linecounts=()
for file in *; do
files+=("$file")
linecounts+=("$(wc -l < "$file")")
done
for i in "${!files[#]}" ;do
echo "${linecounts[i]}"
printf '%s %s\n' "${files[i]}" "${linecounts[i]}" ## Another form.
done
Although it can be done simpler as printf '%s\n' "${linecounts[#]}".
wc -l will only output one value, so you don't need to sort it:
for file in *; do
list+="$file "$( wc -l < "$file" )$'\n'
done
echo "$list"
Then, you can use a while loop to read the list line-by-line:
while read file nlines; do
echo $nlines
done <<< "$list"
That while loop is fragile if any filename has spaces. This is a bit more robust:
while read -a words; do
echo ${words[-1]}
done <<< "$list"
I have this
while read -r line
do
echo -e "$line\r"
done <<< $keys
Where $keys, if printed as it is displays 30k rows.
When doing echo as above I get only one line as output.
I need to filter every line and then output it.
Why is that happening (overwriting)
How can I prevent it to happen?
Use More Quotes
while read -r line
do
echo -e "$line\r"
done <<< "$keys"
observe the quotes in <<< "$keys"
Look:
$ printf -v keys '%s\n' "key one" "key two"
$ echo $keys
key one key two
$ # Oh dear
$ # Now with quotes:
$ echo "$keys"
key one
key two
$ # Yeah :)
$ # Same with a here string:
$ while read line; do echo "$line"; done <<< $keys
key one key two
$ # Oh dear :(
$ # Now with quotes:
$ while read line; do echo "Read: $line"; done <<< "$keys"
Read: line one
Read: line two
$ # Done \o/
In this post, you have learned:
to use more quotes,
to use more quotes,
to use more quotes,
to use more quotes,
to use more quotes,
and to use more quotes.
Now remember,
Each time you forget quotes, God kills a kitten
This is happening because of the \r in your echo command.
It will be solved replacing it to:
echo "$line"
It is also important to note that to call the variable you'd better use "$keys" to keep its format.
All together:
while read -r line
do
echo -e "$line"
done <<< "$keys"
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