Reputation: 31
I want to add a new item to a string variable which represents a comma-separated list while preventing to have items twice.
I came up with this python solution:
python -c "import sys ; first = set(sys.argv[1].split(',')) ; all = first.union(set(sys.argv[2:])) ; print ','.join(all)" 1,2 4 3 2
1,3,2,4
In multiple lines:
import sys
first = set(sys.argv[1].split(','))
all = first.union(set(sys.argv[2:]))
print ','.join(all)
While the solution works, it is not pretty well suited for a one-liner in a shell script.
I've tried a couple of things using awk
, sed
or plain bash
but nothing lead to a short but still stable solution. Does anybody has an idea how to express that with a short command line idiom?
Upvotes: 2
Views: 2310
Reputation: 37404
More and more AWK:
$ echo 1,2 3 2 4|awk 'BEGIN {RS="[ ,\n]";OFS=","} {a[$0]=++i} END {for (j in a) printf "%s%s", j, a[j]<NR?OFS:ORS}'
1,2,3,4
Upvotes: 0
Reputation: 3363
Using sed
:
echo 1,2 3 2 4 | sed -r ':a {s/([^, ]+)(.*)\1/\2,\1/;ta}; s/ /,/g; s/,+/,/g; s/^,//'
Upvotes: 0
Reputation: 37404
More AWK:
$ cat test.in
1,2 3 2 4
5,4 3 4 5
$ cat test.in|awk 'BEGIN {FS="[ ,]";OFS=","} {delete a; delete b; n=split($0,a,FS); for(i in a) b[a[i]]=n--; for(i in b) printf "%s%s",i,(b[i]>1)?OFS:ORS}'
1,2,3,4
3,4,5
Upvotes: 0
Reputation: 17041
A bash
option: Keep your list of items in a string variable (list
below) with leading and trailing commas. That way every item begins and ends with a comma, which makes things much easier. To add an item:
new_item=42 # or whatever
list="${list//,${new_item},/,}${new_item},"
The ${list//...}
removes any duplicates (//
means global replacement), then the ${new_item},
pastes the new item onto the end of the list. To add multiple items:
list=",1,2,"
for x in 4 3 2 ; do list="${list//,$x,/,}$x," ; done
To strip the commas off the list, you can use
list="${list#,}"
list="${list%,}"
A one-liner for your specific example above, but extended with more test cases, would be:
bash -c 'list=",$1,"; shift; for f in "$@"; do list="${list//,$f,/,}$f,"; done; list="${list#,}"; list="${list%,}"; echo $list' -- 1,2 3 4 44 444 1 2
Note, though, that most of that line is just getting the list from the arguments and then out to stdout. Inside a script, list="${list//,$x,/,}$x,"
is all you need.
Upvotes: 1
Reputation: 67507
awk
to the rescue!
$ echo -n "1,2 4 3 2" | awk -v RS='[, ]' '!a[$0]++' | paste -sd,
1,2,4,3
Upvotes: 3
Reputation: 157992
It's not a one-liner, but imo still a clean shell solution:
#!/bin/bash
list="1,2"
new_items="4 5 3"
for i in $new_items ; do
# The `\b` does match at word boundaries
! grep -Eq "\b$i\b" <<< "$list" && list="$list,$i"
done
echo "$list"
If you want a one liner from it, you can put it into a function:
function add_items() {
list="$1"
new_items="$2"
for i in $new_items ; do
! grep -Eq "\b$i\b" <<< "$list" && list="$list,$i"
done
echo "$list"
}
Call it like:
add_items '1,2' '1 2 3'
Upvotes: 1
Reputation: 241868
Perl solution:
perl -le 'undef @h{ (split /,/, shift), @ARGV }; $, = ","; print keys %h' 1,2 4 3 2
Upvotes: 1