I need to sort a table based on two columns, one column is numerical and while other is string. I need to have descending order for numerical while I would like to have alphabetical for string. Use of '-r' option for sort does work for numerical but it gets applied for sting too. I wonder how to apply reverse option only for one column and not the other.
Cmd: sort -r -k5 -k3 -k1
Data:
Input
il || 2 | 3 ||
we || 2 | 2 ||
dt || 0 | 2 ||
di || 0 | 2 ||
cs || 16 | 1 ||
fd || 5 | 1 ||
df || 14 | 0 ||
np || 9 | 0 ||
dt || 9 | 0 ||
ta || 0 | 0 ||
rt || 0 | 0 ||
ps || 0 | 0 ||
Expected
il || 2 | 3 ||
we || 2 | 2 ||
di || 0 | 2 ||
dt || 0 | 2 ||
cs || 16 | 1 ||
fd || 5 | 1 ||
df || 14 | 0 ||
dt || 9 | 0 ||
np || 9 | 0 ||
ps || 0 | 0 ||
rt || 0 | 0 ||
ta || 0 | 0 ||
This works for me
$ sort -rk5 -k3 -k1b input
Output:
il || 2 | 3 ||
we || 2 | 2 ||
di || 0 | 2 ||
dt || 0 | 2 ||
cs || 16 | 1 ||
fd || 5 | 1 ||
df || 14 | 0 ||
dt || 9 | 0 ||
np || 9 | 0 ||
ps || 0 | 0 ||
rt || 0 | 0 ||
ta || 0 | 0 ||
Related
I have a file with this format:
User_ID , Place_ID , Rating
U32 , 1305 , 2
U32 , 1276 , 2
U32 , 1789 , 3
U65 , 1985 , 1
U65 , 1305 , 1
U65 , 1276 , 2
I would like to iterate through this file, sort by Place_ID, iterate through repeated values in Place_ID and add the ratings, once the last element of the Place_ID is added, check if value > x and if true, push the Place_ID into an array.
Ex: Place_ID 1305: 2 + 1 / 2 = 1.5 > 1 ----> ids+=($id)
Place_ID 1276: 2 + 2 / 2 = 2 > 1 -----> ids+=($id)
I have tried with
test5 () {
id=0
count=0
rating=0
ids=()
ratings=()
for i in `sort -t',' -k 2 ratings.csv`
do
aux=`echo "$i"| cut -f2 -d','`
if (( $id != $aux )); then
if (( $rating != 0 )); then
rating=`echo "scale=1; $rating / $count" | bc -l`
if (( $(echo "$rating >= 1" | bc -l) )); then
ids+=($id)
ratings+=($rating)
fi
fi
id=$aux
count=0
rating=0
else
rating=$(($rating + `echo "$i"| cut -f3 -d','`))
count=$(($count + 1))
fi
done
echo ${#ids[#]}
echo ${#ratings[#]}
}
EDIT: I think it works, but is there a way to make it better? Something that doesn't force me to use as many if's and count.
Thanks for the help.
This is another option using less if's:
#!/bin/bash
sum=()
count=()
while read -r line; do
place=$(echo "$line" | cut -d',' -f2)
rating=$(echo "$line" | cut -d',' -f3)
sum[$place]=$(echo "$rating + ${sum[$place]-0}" | bc -l)
count[$place]=$((count[$place] + 1))
done < <( sed 1d ratings.csv | sort -t',' -k 2 | tr -d '[:blank:]' )
ratings=()
for place in "${!sum[#]}"; do
ratings[$place]=$(echo "scale=1; ${sum[$place]} / ${count[$place]}" | bc -l)
done
# ratings at this point has the ratings for each place
echo ${!ratings[#]} # place ids
echo ${ratings[#]} # ratings
I'm assuming your ratings.csv has headers that is why this has sed 1d ratings.csv
i input command in the bash shell:
#!/bin/bash
[[ a>2 ]];echo $?
I get 0
and another command:
#!/bin/bash
[[ 3>2 ]];echo $?
I get
bash: unexpected token 283 in conditional command
bash: syntax error near `3>'
Why I get the err ?
How do I use > < in the [[ ]] ?
I am more curious, in [[]] how to use > < . [[ a>2 ]];echo $? do not add spaces, but [[ 3 > 2 ]] must be added space, more curious about the reasons
thank you!
As the man page for bash builtins states: "Each operator and operand
must be a separate argument."
Also your variable a should be $a
Here is example :
# Returns true
$ a=10; [[ "$a" -gt 1 ]]; echo "$?"
0
# Returns false
$ a=10; [[ "$a" -gt 12 ]]; echo "$?"
1
Following may help you
Integer comparison operators
| Operator | Description | Example |
|----------|-----------------------------|----------------------------------------------------------|
| -eq | Is Equal To | if [ $1 -eq 200 ] |
| -ne | Is Not Equal To | if [ $1 -ne 1 ] |
| -gt | Is Greater Than | if [ $1 -gt 15 ] |
| -ge | Is Greater Than Or Equal To | if [ $1 -ge 10 ] |
| -lt | Is Less Than | if [ $1 -lt 5 ] |
| -le | Is Less Than Or Equal To | if [ $1 -le 0 ] |
| == | Is Equal To | if (( $1 == $2 )) [Note: Used within double parentheses] |
| != | Is Not Equal To | if (( $1 != $2 )) |
| < | Is Less Than | if (( $1 < $2 )) |
| <= | Is Less Than Or Equal To | if (( $1 <= $2 )) |
| > | Is Greater Than | if (( $1 > $2 )) |
| >= | Is Greater Than Or Equal To | if (( $1 >= $2 )) |
String comparison operators
| Operator | Description | Example |
|----------|------------------------------------|-----------------|
| = or == | Is Equal To | if [ $1 == $2 ] |
| != | Is Not Equal To | if [ $1 != $2 ] |
| > | Is Greater Than (ASCII comparison) | if [ $1 > $2 ] |
| >= | Is Greater Than Or Equal To | if [ $1 >= $2 ] |
| < | Is Less Than | if [ $1 < $2 ] |
| <= | Is Less Than Or Equal To | if [ $1 <= $2 ] |
| -n | Is Not Null | if [ -n $1 ] |
| -z | Is Null (Zero Length String) | if [ -z $1 ] |
I'm new in shell scripting and am looking to like this output
1 | 1 | odd | Monday
1 | 2 | even |Tuesday
"
"
1 | 7 | odd | Sunday
2 | 1 | odd | Monday
2 | 2 | even| Tuesday
"
"
2 | 7 | odd | Sunday
3 | 1 | odd | Monday
3 | 2 | even| Tuesday
"
"
3 | 7 | odd | Sunday
I tried below script
i=1
j=1
while [ $i -le 3 ]
do
for (( j=1; j<=7; j++ ))
do
if [ $(($j%2)) -eq 0 ];
then
echo "$i | $j | even "
else
echo "$i | $j | odd"
fi
done
((i++))
done
am getting like this Output
1 | 1 | odd
1 | 2 | even
"
"
1 | 7 | odd
2 | 1 | odd
2 | 2 | even
"
"
2 | 7 | odd
3 | 1 | odd
3 | 2 | even
"
"
3 | 7 | odd
Please help me "Week Day Name incremental in fourth column" where can i add the week name.
date utility can display day of week, so you just need to tell her right date. Here is solution which does it with offset relatively to the actual date.
dow=`date +%u`
i=1
j=1
while [ $i -le 3 ]
do
for (( j=1; j<=7; j++ ))
do
offset=$(( $j - $dow ))
dow_str=`date --date="$offset days" +%A`
if [ $(($j%2)) -eq 0 ];
then
echo "$i | $j | even | $dow_str"
else
echo "$i | $j | odd | $dow_str"
fi
done
((i++))
done
You can also do it using array. Store all the days in an array as:
declare -a week=('Sunday' 'Monday' 'Tuesday' 'Wednesday' 'Thursday' 'Friday' 'Saturday');
and then to access it, do it in for loop with the key as j mod 7. This will give you the week day name starting from Monday.
I have a file like this -
1,[test1] Joe,OK
2,[test2] Jack,OK
3,[test3] Tom,FAIL
I am printing the file like this -
cat file | awk -F"," '{ print "||" $1 "||" $2 "||" $3 "||" }'
I would like the output file like this -
|| 1 || Joe || OK ||
|| 2 || Jack || OK ||
|| 3 || Tom || FAIL ||
i.e. [test1] Joe is modified to Joe how can we do something like this on each column.
echo "[test1] Joe" | sed 's/\[.*\]\s//g'
Which gives me only Joe how can I combined this with the other columns?
In awk with the correct spacing:
$ awk -F, '{split($2,a," ");printf "||%s || %-4s || %s ||\n",$1,a[2],$3}' file
|| 1 || Joe || OK ||
|| 2 || Jack || OK ||
|| 3 || Tom || FAIL ||
You was almost there with your attempt, you should take a look at the printf and split functions.
With sed:
sed 's/\[[^]]*\]//;s/^/|| /;s/$/ ||/;s/,/ || /g' input
Output
|| 1 || Joe || OK ||
|| 2 || Jack || OK ||
|| 3 || Tom || FAIL ||
To do in-place start with sed -i
One way:
awk -F, '{$1=OFS""$1;$NF=$NF""OFS;sub(/.* /,"",$2);}1' OFS=" || " file
On running the above:
|| 1 || Joe || OK ||
|| 2 || Jack || OK ||
|| 3 || Tom || FAIL ||
awk -F"," '{gsub(/,\[.*?\]|,|^|$/," || ",$0) }1' your_file
or
perl -plne 's/^|,/ || /g;s/\[.*?\]//g' your_file
tested below:
> cat temp
1,[test1] Joe,OK
2,[test2] Jack,OK
3,[test3] Tom,FAIL
> perl -plne 's/^|,|$/ || /g;s/\[.*?\]//g' temp
|| 1 || Joe || OK ||
|| 2 || Jack || OK ||
|| 3 || Tom || FAIL ||
> awk -F"," '{gsub(/,\[.*?\]|,|^|$/," || ",$0) }1' temp
|| 1 || Joe || OK ||
|| 2 || Jack || OK ||
|| 3 || Tom || FAIL ||
>
If you want to replace the file inplace use:
perl -i -plne 's/^|,|$/ || /g;s/\[.*?\]//g' your_file
I have a file with some data like this:
1, 3, 0, 0, 0
0, 4, 5, 0, 5
2, 6, 0, 1, 0
I would like to write a shell script to delete all the lines where the 3rd argument is 0 (here line 1 and 2)
I already know that
sed -i '/0/d' file.txt
but I don't know how I can select the 3rd arguments whatever the others one.
Do you have an idea?
Awk may be a better tool than sed for this task:
awk -F' *, *' '$3 != 0 {print}' FILE
But sed can do it:
sed -i '/^[0-9][0-9]*, [0-9][0-9]*, 0,/d' FILE
sed -iE '/^([^,]+,){2} 0,/d' file
explained with explain.py:
sed -iE '/^([^,]+,){2} 0,/d' file
\_/ || ||| || || \_/ | |
| || ||| || || | | \- delete command
| || ||| || || | |
| || ||| || || | \- followed by blank zero komma
| || ||| || || |
| || ||| || || \- two times this inner pattern
| || ||| || ||
| || ||| || |\- followed by a comma
| || ||| || |
| || ||| || \- at least one of them
| || ||| ||
| || ||| |\- komma
| || ||| |
| || ||| \- not
| || |||
| || ||\- start of pattern, group of characters, which are
| || ||
| || |\- at begin of line
| || |
| || \- start of pattern for delete command
| ||
| |\- Extended regexp (parenthesis and braces without masking)
| |
| \- inplace changes
|
\- run sed
awk with in place editing (similar to sed -i)
awk '$3!="0,"{print $0>FILENAME}' file
Ruby(1.9+)
ruby -ane 'print unless $F[2]=="0," ' file