我有一个重复单词的字符串。我想只显示独特的单词。字符串是:
variable="alpha bravo charlie alpha delta echo charlie"
我知道有几种工具可以一起做到这一点。这就是我想到的:
echo $variable | tr " " "\n" | sort -u | tr "\n" " "
有什么更有效的方法呢?
答案 0 :(得分:3)
您可以使用xargs
:
echo "$variable" | xargs -n 1 | sort -u | xargs
答案 1 :(得分:2)
注意:此解决方案假定所有唯一字应按照输入中遇到的顺序输出。相比之下,OP自己的解决方案尝试输出排序的唯一字词列表。
一个简单的仅限Awk的解决方案(符合POSIX标准),它通过避免管道(总是涉及子单元)来高效。
awk -v RS=' ' '{ if (!seen[$1]++) { printf "%s%s",sep,$1; sep=" " } }' <<<"$variable"
# The above prints without a trailing \n, as in the OP's own solution.
# To add a trailing newline, append `END { print }` to the end
# of the Awk script.
请注意$variable
如何双引号以防止意外shell expansions,特别是路径名扩展(globbing),以及如何通过它提供给Awk here-string(<<<
)。
-v RS=' '
告诉Awk将输入分成记录一个空格。
$0
- 整个记录的原因 - 但{ {1}},记录的第一个字段,由于Awk的默认字段拆分行为而删除了换行符。 $1
是一种常见的Awk习惯用法,如果它尚不存在,则会在关联数组seen[$1]++
中为输入字$1
创建一个条目,或增加其发生次数。
seen
因此仅对给定单词的第一个出现返回true(其中!seen[$0]++
隐式为零/空字符串; {{1} }是一个帖子 - 增量,因此在 评估条件之后才生效
seen[$0]
打印手边的单词++
,前面是分隔符{printf "%s%s",sep,$1; sep=" "}
,它隐式地是的空字符串单词,但是后续单词的单个空格,因为在$1
之后立即将sep
设置为sep
。
这是一个更灵活的变体,处理输入词之间的任何空格;它适用于 GNU Awk和 Mawk [1] :
" "
awk -v RS='[[:space:]]+' '{if (!seen[$0]++){printf "%s%s",sep,$0; sep=" "}}' <<<"$variable"
告诉Awk通过空格,制表符和换行符的任意组合将输入拆分为记录。 [1]不幸的是,BSD / OSX Awk(严格遵守POSIX spec),不支持使用正则表达式甚至多字符文字作为-v RS='[[:space:]]s+'
,输入记录分隔符。
答案 2 :(得分:2)
以下shell parameter expansion将使用换行符替换空格,然后将结果传递到 sort 实用程序,以仅返回唯一的单词。
$ echo -e "${variable// /\\n}" | sort -u
alpha
bravo
charlie
delta
echo
这会产生排序单词的副作用,因为 sort 和 uniq 实用程序都需要对输入进行排序以检测重复项。如果那不是你想要的,我还发布了Ruby solution,保留了原始的单词顺序。
如果像一位评论者所指出的那样,您正在尝试将您的独特单词重新组合成一行,则可以使用command substitution来执行此操作。例如:
$ echo $(echo -e "${variable// /\\n}" | sort -u)
alpha bravo charlie delta echo
命令替换缺乏引号是故意的。如果你引用它,新行将被保留,因为Bash不会word-splitting。不带引号,shell会将结果作为单行返回,但看起来不直观。
答案 3 :(得分:2)
我已经发布了Bash-specific answer,但如果你想在保留原始字符串的单词顺序时只返回唯一的单词,那么你可以使用以下的Ruby单行代码:
$ echo "$variable" | ruby -ne 'puts $_.split.uniq'
alpha
bravo
charlie
delta
echo
这会将输入字符串拆分为空格,然后从结果数组中返回唯一元素。
与 sort 或 uniq 实用程序不同,Ruby不需要对单词进行排序以检测重复项。如果您不希望对结果进行排序,这可能是一个更好的解决方案,尽管根据您的输入示例,它对发布的示例没有实际区别。
如果像一位评论者指出的那样,你在重复数据删除后尝试将这些单词重新组合成一行,那么你也可以这样做。为此,我们只需附加Array#join方法:
$ echo "$variable" | ruby -ne 'puts $_.split.uniq.join(" ")'
alpha bravo charlie delta echo
答案 4 :(得分:1)
使用BASH 4+中的关联数组,您可以简化:
variable="alpha bravo charlie alpha delta echo charlie"
# declare an associative array
declare -A unq
# read sentence into an indexed array
read -ra arr <<< "$variable"
# iterate each word and populate associative array with word as key
for w in "${arr[@]}"; do
unq["$w"]=1
done
# print unique results
printf "%s\n" "${!unq[@]}"
delta
bravo
echo
alpha
charlie
## if you want results in same order as original string
for w in "${arr[@]}"; do
[[ ${unq["$w"]} ]] && echo "$w" && unset unq["$w"]
done
alpha
bravo
charlie
delta
echo
答案 5 :(得分:1)
您可以使用awk:
$ echo "$variable" | awk '{for(i=1;i<=NF;i++){if (!seen[$i]++) printf $i" "}}'
alpha bravo charlie delta echo
如果您不想要尾随空格并想要一个尾随CR,您可以这样做:
$ echo "$variable" | awk 'BEGIN{j=""} {for(i=1;i<=NF;i++){if (!seen[$i]++)j=j==""?j=$i:j=j" "$i}} END{print j}'
alpha bravo charlie delta echo
答案 6 :(得分:-1)
for x in $vaviable; do
if [ "$(eval echo $(echo \$un__$x))" = "" ]; then
echo -n $x
eval un__$x=1
__usv="$__usv un__$x"
fi
done
unset $__usv