I am attempting to create a program in Unix that accesses a data file, adding, deleting, and searching within the file for names and usernames. With this if statement, I am attempting to allow the user to search for data in the file by the first field.
All of the data in the file uses uppercase letters, so I first must convert any text the user input from lowercase to uppercase letters. For some reason, this code is not working with both converting to uppercase and searching and printing the data.
How can I fix it?
if [ "$choice" = "s" ] || [ "$choice" = "S" ]; then
tput cup 3 12
echo "Enter the first name of the user you would like to search for: "
tput cup 4 12; read search | tr '[a-z]' '[A-Z]'
echo "$search"
awk -F ":" '$1 == "$search" {print $3 " " $1 " " $2 }'
capstonedata.txt
fi
This: read search | tr '[a-z]' '[A-Z]' will not assign anything to variable search.
It should be something like
read input
search=$( echo "$input" | tr '[a-z]' '[A-Z]' )
and it is better to use parameter expansion for case modification:
read input
search=${input^^}
If you use Bash, you can declare a variable to convert to uppercase:
$ declare -u search
$ read search <<< 'lowercase'
$ echo "$search"
LOWERCASE
As for your code, read doesn't have any output, so piping to tr doesn't do anything, and you can't have a newline before the file name in the awk statement.
Edited version of your code, minus all the tput stuff:
# [[ ]] to enable pattern matching, no need to quote here
if [[ $choice = [Ss] ]]; then
# Declare uppercase variable
declare -u search
# Read with prompt
read -p "Enter the first name of the user you would like to search for: " search
echo "$search"
# Proper way of getting variable into awk
awk -F ":" -v s="$search" '$1 == s {print $3 " " $1 " " $2 }' capstonedata.txt
fi
Alternatively, if you want to use only POSIX shell constructs:
case $choice in
[Ss] )
printf 'Enter the first name of the user you would like to search for: '
read input
search=$(echo "$input" | tr '[[:lower:]]' '[[:upper:]]')
awk -F ":" -v s="$search" '$1 == s {print $3 " " $1 " " $2 }' capstonedata.txt
;;
esac
Awk is not shell (google that). Just do:
if [ "$choice" = "s" ] || [ "$choice" = "S" ]; then
read search
echo "$search"
awk -F':' -v srch="$search" '$1 == toupper(srch) {print $3, $1, $2}' capstonedata.txt
fi
Related
Example:
aa\tab\tac\tad\tae
ba\tbb\tbc
ca\tcb\tcc
da\tdb\tdc\tdd
Expected output:
ba\tbb\tbc
ca\tcb\tcc
I want to extract lines containing blank in a 4th column with linux command. If you know the command, could you let me know it?
Using awk
$ awk -F"\\" '{if($4=="") print}' input_file
ba\tbb\tbc
ca\tcb\tcc
You can use cut and use "" as delimiter and check the field 4 if it is not empty. Then use the grep -v to check if a trailing "" exists or not.
in an If statement you can get the result.
text='aa\tab\tac\'
if [[ -z $(echo $text | cut -d \\ -f 4 | grep -v \\\\\$) ]]; then
if[[ -z $(echo $text | grep -v \\\\\$) ]]; then
echo $text
else
echo 'Text has a slash at the end'
fi
else
echo 'Not valid input'
fi
I am facing some issue when I am reading the 3rd word(a hex string) of each line in a text file and compare it with a hex number. Can some one please help me on it.
#!/bin/bash
A=$1
cat $A | while read a; do
a1=$(echo \""$a"\" | awk '{ print $3 }')
#echo $a > cut -d " " -f 3
echo $a1
(("$a1" == 0x10F7))
echo $?
done
But when I use below, the comparison happens correctly,
a1= 0xADCAFE
(( "$a1" == 0x10F7 ))
echo $?
Then why it is showing issue when I read like below,
a1=$(echo \""$a"\" | awk '{ print $3 }')
or> a1=$(echo $a | awk '{ print $3 }')
echo $a prints intended hex value, but comparison does not happen.
Regards,
Running Awk inside a while read loop is an antipattern. Just do the loop in Awk; it's good at that.
awk '$3 == 4343' "$1"
If you want to compare against a string whose value is "0x10F7" then it's
awk '$3 == "0x10F7"' "$1"
If you want to match either, case insensitively etc, a regex is a good way to do that.
awk '$3 ~ /^(0x10[Ff]7|4343)$/' "$1"
Notice how the $1 in double quotes is handled by the shell, and gets replaced by a (properly quoted!) copy of the script's first command-line argument before Awk runs, while the Awk script in single quotes has its own namespace, so $3 is an Awk variable which refers to the third field in the current input line.
Either way, avoid the useless use of cat and always always always quote variables which contain file names with double quotes.
That's literal double quotes. You seem to have tried both a dangerous bare $a and a doubly double-quoted "\"$a\"" where the simple "$a" would be what you actually want.
Thank you all for your responses, Now my script is working fine. I was trying to match two files, below script does the purpose
#!/bin/bash
A=$1
B=$2
dos2unix -f "$A"
dos2unix -f "$B"
rm search_match.txt search_data_match.txt search_nomatch.txt search_data_nomatch.txt
while read line;do
search_word=$(echo $line | awk '{ print $1 }')
grep "$search_word" $B >> temp_file.txt
while read var;do
file1_hex=$(echo $line | awk '{ print $2 }')
file2_hex=$(echo $var | awk '{ print $3 }')
(("$file1_hex" == "$file2_hex"))
zero=$(echo $?)
if [ "$zero" -eq 0 ] ; then
echo $line >> search_match.txt
echo $var >> search_data_match.txt
else
echo $line >> search_nomatch.txt
echo $var >> search_data_nomatch.txt
fi
done < "temp_file.txt"
rm temp_file.txt
done < "$A"
I am making a bash script. I have to get 3 variables
VAR1=$(cat /path to my file/ | grep "string1" | awk '{ print $2 }'
VAR2=$(cat /path to my file/ | grep "string2" | awk '{ print $2 }'
VAR3=$(cat /path to my file/ | grep "string3" | awk '{ print $4 }'
My problem is that if I write
echo $VAR1
echo $VAR2
echo $VAR3
I can see values correctly
But when I try to write them in one line like this
echo "VAR1: $VAR1 VAR2: $VAR2 VAR3: $VAR3"
Value from $VAR3 is written at the beginning of output overwritting values of $VAR1 and $VAR2
I expect my explanation had been clear. Any doubt please let me know
Thanks and regards.
Rambert
It seems to me that $VAR3 contains \r which in some shells will move the cursor to the beginning of the line. Use printf instead:
printf "VAR1: %s VAR2: %s VAR3: %s\n" "$VAR1" "$VAR2" "$VAR3"
Also note that the way you extract the values is highly inefficient and can be reduced to one call to awk:
read -r var1 var2 var3 _ < <(awk '/string1/ { a=$2 }
/string2/ { b=$2 }
/string3/ { c=$4 }
END { print(a, b, c) }' /path/to/file)
printf "VAR1: %s VAR2: %s VAR3: %s\n" "$var1" "$var2" "$var3"
A nitpick is that uppercase variable names are reserved for environment variables, so I changed all to lowercase.
<(...) is a process substitution and will make ... write to a "file" and return the file name:
$ echo <(ls)
/dev/fd/63
And command < file is a redirection changing standard input of command to be comming from the file file.
You could write :
cat /path to my file/ | grep "string1" | awk '{ print $2 }'
as
awk '/string1/{print $2}' /path/to/file
In other words you could do with awk alone what you intended to do with cat, grep & awk
So finally get :
VAR1=$(awk '/string1/{print $2}' /path/to/file) #mind the closing ')'
Regarding the issue you face, it looks like you have carriage returns or \r in your variables. In bash echo will not interpret escape sequences without the -e option, but the printf option which [ #andlrc ] pointed out is a good try though as he mentioned in his [ answer ]
which in some shells will move the cursor to the beginning
Notes :
Another subtle point to keep in mind is to avoid using upper case variable names like VAR1 for user scripts. So replace it with var1 or so
When assigning values to variable spaces are not allowed around =, so
VAR1="Note there are no spaces around = sign"
is the right usage
I'm new to bash scripting, I'm learning how commands work, I stumble in this problem,
I have a file /home/fedora/file.txt
Inside of the file is like this:
[apple] This is a fruit.
[ball] This is a sport's equipment.
[cat] This is an animal.
What I wanted is to retrieve words between "[" and "]".
What I tried so far is :
while IFS='' read -r line || [[ -n "$line" ]];
do
echo $line | awk -F"[" '{print$2}' | awk -F"]" '{print$1}'
done < /home/fedora/file.txt
I can print the words between "[" and "]".
Then I wanted to put the echoed word into a variable but i don't know how to.
Any help I will appreciate.
Try this:
variable="$(echo $line | awk -F"[" '{print$2}' | awk -F"]" '{print$1}')"
or
variable="$(awk -F'[\[\]]' '{print $2}' <<< "$line")"
or complete
while IFS='[]' read -r foo fruit rest; do echo $fruit; done < file
or with an array:
while IFS='[]' read -ra var; do echo "${var[1]}"; done < file
In addition to using awk, you can use the native parameter expansion/substring extraction provided by bash. Below # indicates a trim from the left, while % is used to trim from the right. (note: a single # or % indicates removal up to the first occurrence, while ## or %% indicates removal of all occurrences):
#!/bin/bash
[ -r "$1" ] || { ## validate input is readable
printf "error: insufficient input. usage: %s filename\n" "${0##*/}"
exit 1
}
## read each line and separate label and value
while read -r line || [ -n "$line" ]; do
label=${line#[} # trim initial [ from left
label=${label%%]*} # trim through ] from right
value=${line##*] } # trim from left through '[ '
printf " %-8s -> '%s'\n" "$label" "$value"
done <"$1"
exit 0
Input
$ cat dat/labels.txt
[apple] This is a fruit.
[ball] This is a sport's equipment.
[cat] This is an animal.
Output
$ bash readlabel.sh dat/labels.txt
apple -> 'This is a fruit.'
ball -> 'This is a sport's equipment.'
cat -> 'This is an animal.'
i have an input file as follow:
some lines with quote and :
AGE:23
some lines with quote and :
NAME:2,0,"My Name Is"
some lines with quote and :
Actually i use this code to extract information from the file:
age="$(cat "$file" | awk -F ':' '/AGE:/ { print $2 }')"
name="$(cat "$file" | awk -F '"' '/NAME:/ { print $2 }' )"
echo "age: $age"
echo "name: $name"
output:
age: 23
name: My Name Is
i'm searching for a better way to do this than running cat and awk two times. i have search to do it in one cat/awk line but can't figure it out, not appropriated in this case? can anyone point me a better way please ?
Thanks in advance
while IFS=: read key value; do
case $key in
AGE) age=$value;;
NAME) name=$(awk -F'"' '{print $2}' <<< "$value");;
esac
done < "$file"
I like #JohnKugelman's approach, but it can be improved: use colon and quote as the field separators:
while IFS=':"' read -ra fields; do
case ${fields[0]} in
AGE) age=${fields[1]} ;;
NAME) [[ ${fields[1]} == "2,0," ]] && name=${fields[2]} ;;
esac
done < file
With awk, I'd write:
read age name < <(
awk -F '[:,]' '
$1 == "AGE" {printf "%s ",$2}
$1 == "NAME" && $2 == 2 && $3 == 0 {printf "%s ",$NF}
END {print ""}
' filename
)
If the data is simple as you have shown in your question. No need to use shell for this , just awk will be more than enough
awk -F '"' '/AGE/{print tolower($0)}/NAME/{print "name:"$2}' input.txt