I have a full names that have been read into arrays. I am trying to create a file using only the last name; the last name might have white spaces that should be replaced by underscores. My thought is to create a string of the file name and then create the file. I have already taken care of the cases with only one last name. I am having trouble with the last names with white spaces. This is what I have so far:
if [ "${#name_arr[#]}" -eq 2 ]; then
for i in "${name_arr[#]:1}";do # :1 because first element is their first name
last_name=$i
done
echo $last_name
else
for i in "${name_arr[#]:1}";do
last_name=${last_name}_${i}
done
echo $last_name
fi
The output of this concatenates all of the names with underscores. So instead of:
Doe
Austen
Vaughn_Williams
Doe
It is echoing:
Doe
Austen
Austen_Vaughn_Williams
Doe
You don't need loops, or nor do you need to check the length of the list. Just join all but the first element with a space to get the last name.
last_name=${name_arr[*]:1} # Assuming the default value of IFS
last_name=${last_name// /_}
At the cost of a fork, you can do this in one line.
last_name=$(IFS='_'; echo "${name_arr[*]:1}")
Try this approach
if [ "${#name_arr[#]}" -eq 2 ]; then
for i in "${name_arr[#]:1}";do # :1 because first element is their first name
last_name=$i
done
echo $last_name
else
last_name=${name_arr[1]}
for i in "${name_arr[#]:2}";do
last_name=${last_name}_${i}
done
echo $last_name
fi
First, take the 2nd element of the name_arr in the last_name, and add the remaining elements of the array in to the last_name variable with a loop
Related
I'm stuck on a bash problem. The script should read each line in data.txt and assign it to an array before checking if each array value is empty. If the array value is empty, the text "unknown" should replace the empty value. Line 2 would otherwise have the text $Author.
data.txt
07/22/2022, 00:00:00
NASA Reveals Webb Telescope's first images of unseen universe
"We are elated to celebrate this extraordinary day with the world," said Greg Robinson, Webb program director at NASA Headquarters.
webb, telescopes, images, reveals, galaxies, star, unseen, nasa, universe, nebula, webbs, gas, view, space
bashscript.sh
# assign each line in data.txt to array
readarray data < data.txt
# for each value in array
for i in "${data[#]}"; do
# check if value is null
if [ -z "$data[i]" ]; then
# if value is null, assign text "unknown" to value
data[i]="unknown"
fi
done
Author=${data[1]}
echo "author: $Author"
ouput:
author:
expected output:
author: unknown
Thanks for reading/helping....
The main problem with your script is that a string consisting of a new-line character isn't considered a null string. The check for the empty line should have been if [ "${data[i]}" = $'\n' ]. Also you should have used a C-style for loop. Below is a corrected version of your script:
#!/bin/bash
readarray data < data.txt
# for each value in array
for ((i = 0; i < ${#data[#]}; ++i)); do
# check if value is null
if [ "${data[i]}" = $'\n' ]; then
# if value is null, assign text "unknown" to value
data[i]="unknown"
fi
done
Author=${data[1]}
echo "author: $Author"
Or, as suggested by glenn jackman, use the readarry with the -t flag and check for the empty string as [ -z "${data[i]}" ]
In bash, try to avoid explicit iteration over the data. Such an approach is not only slow, but also prone to errors, especially for beginners. Instead, use tools designed for the job, which will do the looping for you.
sed 's/^$/unknown/' puts the string unknown into all empty lines.
To retrieve a single line, you can use head | tail or sed again.
Your entire script can be boiled down to
sed -n '2{s/^$/unknown/;s/^/author: /;p;q;}' data.txt
I have wrote a script that throws the output of running node processes with the cwd of that process and I store the value in an array using for loop and do echo that array.
How can I able to get the user enter the index of array regarding the output that the script throws and show the output against that input generated by user
Example Myscript
array=$(netstat -nlp | grep node)
for i in ${array[*]}
do
echo $i
done
output is something like that
1056
2064
3024
I want something more advance. I want to take input from user like
Enter the regarding index from above list = 1
And lets suppose user enter 1
Then next output should be
Your selected value is 2064
Is it possible in bash
First, you're not actually using an array, you are storing a plain string in the variable "array". The string contains words separated by whitespace, so when you supply the variable in the for statement, the unquoted value is subject to Word Splitting
You need to use the array syntax for setting the array:
array=( $(netstat -nlp | grep node) )
However, the unquoted command substitution still exposes you to Filename Expansion. The best way to store the lines of a command into an array is to use the mapfile command with a process substitution:
mapfile -t array < <(netstat -nlp | grep node)
And in the for loop, make sure you quote all the variables and use index #
for i in "${array[#]}"; do
echo "$i"
done
Notes:
arrays created with mapfile will start at index 0, so be careful of off-by-one errors
I don't know how variables are implemented in bash, but there is this oddity:
if you refer to the array without an index, you'll get the first element:
array=( "hello" "world" )
echo "$array" # ==> hello
If you refer to a plain variable with array syntax and index zero, you'll get the value:
var=1234
echo "${var[0]}" # ==> 1234
I know that reading a .csv file can be done simply in bash with this loop:
#!/bin/bash
INPUT=data.cvs
OLDIFS=$IFS
IFS=,
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }
while read flname dob ssn tel status
do
echo "Name : $flname"
echo "DOB : $dob"
echo "SSN : $ssn"
echo "Telephone : $tel"
echo "Status : $status"
done < $INPUT
IFS=$OLDIFS
But I want to slightly modify this- I want to make the columns be defined by the programmer in the bash file.
For example:
declare -a columns=("Name", "Surname", "ID", "Gender")
while read columns
do
//now echo everything that has been read
done < $INPUT
So I want to specify the list of variables that should be used as the container to the read CSV data with an array and then access this array inside the while body.
Is there a way to do it?
The key to this solution is the comment before the while statement below. read is a built-in, but it is still a command, and command arguments are expanded by the shell before executing the command. After expansion of ${columns[#]}, the command becomes
read Name Surname ID Gender
Example:
# Don't use commas in between array values (since they become part of the value)
# Values not quoted because valid names don't need quotes, and these
# value must be valid names
declare -a columns=(Name Surname ID Gender)
Then, we can try:
# Read is a command. Arguments are expanded.
# The quotes are unnecessary but it's hard to break habits :)
while read "${columns[#]}"; do
echo Name is "$Name"
# etc
done <<< "John Doe 27 M"
Output:
Name is John
This same approach would work even in a shell without arrays; the column names can just be a space separated list. (Example run in dash, a Posix shell)
$ columns="Name Surname ID Gender"
$ # Here it is vital that $columns not be quoted; we rely on word-splitting
$ while read $columns; do
> echo Name is $Name
> done
John Doe 27 M
Name is John
...
Read the line into an array, then loop through that array and create an associative array that uses the column names.
while read -r line
do
vals=($line)
declare -A colmap
i=0
for col in ${columns[#]}
do
colmap[col]=${vals[$i]}
let i=i+1
done
# do stuff with colmap here
# ...
unset colmap # Clear colmap before next iteration
done < $INPUT
I am trying to get multiple words/arguments into one variable with read. I tried assigning it into an array and using while loop to put all the elements in the array into 1 string.
read -a info
i=0
datastring=""
while [ $i -lt ${info[#]} ]
do
datastring=$datastring${info[i]}
done
echo "$dataString"
When I run the program it just doesn't do anything and sits there and won't print out datastring and I'm kinda lost on any other way to do it.
read datastring <<<"this sentence contains multiple words"
echo "$datastring"
If you already have an array
datastring=${info[*]}
Will concatenate the array into a single word, using the 1st char of $IFS as a separator. If you want the words all smushed together with no separators, you could do this:
datastring=""
for word in "${info[#]}"; do datastring+=$word; done
or this:
datastring=$(IFS=""; echo "${info[*]}")
or this:
datastring=${info[*]}
datastring=${datastring// /}
Note, all quotes and array indices (* vs #) have been carefully chosen: see
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion and
http://www.gnu.org/software/bash/manual/bashref.html#Arrays
I want to store some lines of the output of blkid in an array. The problem is, that those lines contain whitespace and the array syntax takes those as delimiters for single array elements, so that i end up with splitted lines in my array instead of one line beeing one array element.
This is the code i currently have:
devices=($(sudo blkid | egrep '^/dev/sd[b-z]'))
echo ${devices[*]} gives me the following output:
/dev/sdb1: LABEL="ARCH_201108" TYPE="udf"
/dev/sdc1: LABEL="WD" UUID="414ECD7B314A557F" TYPE="ntfs"
But echo ${#devices[*]} gives me 7 but insted i want to have 2. I want /dev/sdb1: LABEL="ARCH_201108" TYPE="udf" to be the first element in my devices array and /dev/sdc1: LABEL="WD" UUID="414ECD7B314A557F" TYPE="ntfs" to be the second one. How can i accomplish that?
Array elements are split on the IFS value. If you want to split on newline, adjust IFS:
IFS_backup=$IFS
IFS=$'\n'
devices=($(sudo blkid | egrep '^/dev/sd[b-z]'))
IFS=$IFS_backup
echo ${#devices[#]}